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
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/blender/blendthumb/CMakeLists.txt1
-rw-r--r--source/blender/blenfont/intern/blf_font.c38
-rw-r--r--source/blender/blenfont/intern/blf_font_win32_compat.c6
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c2
-rw-r--r--source/blender/blenkernel/BKE_action.h11
-rw-r--r--source/blender/blenkernel/BKE_anim_path.h28
-rw-r--r--source/blender/blenkernel/BKE_armature.h3
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh423
-rw-r--r--source/blender/blenkernel/BKE_attribute_math.hh109
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_callbacks.h60
-rw-r--r--source/blender/blenkernel/BKE_collection.h8
-rw-r--r--source/blender/blenkernel/BKE_context.h19
-rw-r--r--source/blender/blenkernel/BKE_curve.h39
-rw-r--r--source/blender/blenkernel/BKE_displist.h45
-rw-r--r--source/blender/blenkernel/BKE_editmesh.h3
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h21
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h19
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh395
-rw-r--r--source/blender/blenkernel/BKE_geometry_set_instances.hh15
-rw-r--r--source/blender/blenkernel/BKE_global.h14
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h5
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h7
-rw-r--r--source/blender/blenkernel/BKE_image.h1
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h17
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h16
-rw-r--r--source/blender/blenkernel/BKE_lib_query.h5
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h4
-rw-r--r--source/blender/blenkernel/BKE_material.h10
-rw-r--r--source/blender/blenkernel/BKE_mesh.h9
-rw-r--r--source/blender/blenkernel/BKE_mesh_boolean_convert.hh43
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.hh55
-rw-r--r--source/blender/blenkernel/BKE_modifier.h8
-rw-r--r--source/blender/blenkernel/BKE_nla.h9
-rw-r--r--source/blender/blenkernel/BKE_node.h78
-rw-r--r--source/blender/blenkernel/BKE_node_ui_storage.hh20
-rw-r--r--source/blender/blenkernel/BKE_object.h12
-rw-r--r--source/blender/blenkernel/BKE_paint.h6
-rw-r--r--source/blender/blenkernel/BKE_persistent_data_handle.hh153
-rw-r--r--source/blender/blenkernel/BKE_scene.h3
-rw-r--r--source/blender/blenkernel/BKE_softbody.h2
-rw-r--r--source/blender/blenkernel/BKE_spline.hh551
-rw-r--r--source/blender/blenkernel/BKE_unit.h13
-rw-r--r--source/blender/blenkernel/BKE_volume.h42
-rw-r--r--source/blender/blenkernel/BKE_volume_render.h8
-rw-r--r--source/blender/blenkernel/CMakeLists.txt28
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc24
-rw-r--r--source/blender/blenkernel/intern/action.c42
-rw-r--r--source/blender/blenkernel/intern/action_mirror.c457
-rw-r--r--source/blender/blenkernel/intern/anim_data.c14
-rw-r--r--source/blender/blenkernel/intern/anim_path.c410
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c548
-rw-r--r--source/blender/blenkernel/intern/appdir.c2
-rw-r--r--source/blender/blenkernel/intern/armature.c29
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c16
-rw-r--r--source/blender/blenkernel/intern/armature_pose.cc10
-rw-r--r--source/blender/blenkernel/intern/armature_update.c589
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc907
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh230
-rw-r--r--source/blender/blenkernel/intern/attribute_math.cc15
-rw-r--r--source/blender/blenkernel/intern/blender.c2
-rw-r--r--source/blender/blenkernel/intern/blendfile.c5
-rw-r--r--source/blender/blenkernel/intern/brush.c13
-rw-r--r--source/blender/blenkernel/intern/bvhutils.c4
-rw-r--r--source/blender/blenkernel/intern/collection.c141
-rw-r--r--source/blender/blenkernel/intern/constraint.c59
-rw-r--r--source/blender/blenkernel/intern/context.c51
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.cc9
-rw-r--r--source/blender/blenkernel/intern/curve.c114
-rw-r--r--source/blender/blenkernel/intern/curve_bevel.c28
-rw-r--r--source/blender/blenkernel/intern/curve_convert.c2
-rw-r--r--source/blender/blenkernel/intern/curve_deform.c86
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc272
-rw-r--r--source/blender/blenkernel/intern/curveprofile.c3
-rw-r--r--source/blender/blenkernel/intern/customdata.c4
-rw-r--r--source/blender/blenkernel/intern/customdata_file.c10
-rw-r--r--source/blender/blenkernel/intern/deform.c11
-rw-r--r--source/blender/blenkernel/intern/displist.cc (renamed from source/blender/blenkernel/intern/displist.c)892
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c2
-rw-r--r--source/blender/blenkernel/intern/editmesh.c11
-rw-r--r--source/blender/blenkernel/intern/editmesh_tangent.c2
-rw-r--r--source/blender/blenkernel/intern/effect.c8
-rw-r--r--source/blender/blenkernel/intern/fcurve.c74
-rw-r--r--source/blender/blenkernel/intern/fcurve_cache.c189
-rw-r--r--source/blender/blenkernel/intern/fluid.c6
-rw-r--r--source/blender/blenkernel/intern/font.c10
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc1199
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc110
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc594
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc73
-rw-r--r--source/blender/blenkernel/intern/geometry_component_volume.cc14
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc94
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc347
-rw-r--r--source/blender/blenkernel/intern/gpencil.c45
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c4
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c184
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c10
-rw-r--r--source/blender/blenkernel/intern/idprop.c2
-rw-r--r--source/blender/blenkernel/intern/image.c31
-rw-r--r--source/blender/blenkernel/intern/image_gen.c21
-rw-r--r--source/blender/blenkernel/intern/image_gpu.c24
-rw-r--r--source/blender/blenkernel/intern/key.c9
-rw-r--r--source/blender/blenkernel/intern/lattice.c62
-rw-r--r--source/blender/blenkernel/intern/lattice_deform.c22
-rw-r--r--source/blender/blenkernel/intern/lattice_deform_test.cc1
-rw-r--r--source/blender/blenkernel/intern/lib_id.c120
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc173
-rw-r--r--source/blender/blenkernel/intern/lib_override.c951
-rw-r--r--source/blender/blenkernel/intern/lib_query.c46
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c22
-rw-r--r--source/blender/blenkernel/intern/main.c4
-rw-r--r--source/blender/blenkernel/intern/mask.c9
-rw-r--r--source/blender/blenkernel/intern/material.c166
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.c9
-rw-r--r--source/blender/blenkernel/intern/mesh.c4
-rw-r--r--source/blender/blenkernel/intern/mesh_boolean_convert.cc219
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c90
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c7
-rw-r--r--source/blender/blenkernel/intern/mesh_fair.cc2
-rw-r--r--source/blender/blenkernel/intern/mesh_mirror.c15
-rw-r--r--source/blender/blenkernel/intern/mesh_remap.c3
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.cc158
-rw-r--r--source/blender/blenkernel/intern/mesh_tangent.c2
-rw-r--r--source/blender/blenkernel/intern/modifier.c6
-rw-r--r--source/blender/blenkernel/intern/movieclip.c2
-rw-r--r--source/blender/blenkernel/intern/nla.c62
-rw-r--r--source/blender/blenkernel/intern/node.cc200
-rw-r--r--source/blender/blenkernel/intern/node_ui_storage.cc29
-rw-r--r--source/blender/blenkernel/intern/object.c131
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc (renamed from source/blender/blenkernel/intern/object_dupli.c)442
-rw-r--r--source/blender/blenkernel/intern/object_update.c7
-rw-r--r--source/blender/blenkernel/intern/ocean.c10
-rw-r--r--source/blender/blenkernel/intern/particle.c9
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c2
-rw-r--r--source/blender/blenkernel/intern/particle_system.c16
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h28
-rw-r--r--source/blender/blenkernel/intern/pointcache.c2
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c6
-rw-r--r--source/blender/blenkernel/intern/scene.c89
-rw-r--r--source/blender/blenkernel/intern/screen.c70
-rw-r--r--source/blender/blenkernel/intern/softbody.c37
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc380
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc592
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc444
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc124
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg.c154
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c3
-rw-r--r--source/blender/blenkernel/intern/text.c19
-rw-r--r--source/blender/blenkernel/intern/tracking.c5
-rw-r--r--source/blender/blenkernel/intern/unit.c10
-rw-r--r--source/blender/blenkernel/intern/volume.cc280
-rw-r--r--source/blender/blenkernel/intern/volume_render.cc6
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c551
-rw-r--r--source/blender/blenkernel/nla_private.h11
-rw-r--r--source/blender/blenlib/BLI_asan.h2
-rw-r--r--source/blender/blenlib/BLI_color.hh302
-rw-r--r--source/blender/blenlib/BLI_compiler_attrs.h7
-rw-r--r--source/blender/blenlib/BLI_endian_defines.h38
-rw-r--r--source/blender/blenlib/BLI_enumerable_thread_specific.hh73
-rw-r--r--source/blender/blenlib/BLI_fileops.h20
-rw-r--r--source/blender/blenlib/BLI_float3.hh7
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh44
-rw-r--r--source/blender/blenlib/BLI_function_ref.hh24
-rw-r--r--source/blender/blenlib/BLI_hash.hh17
-rw-r--r--source/blender/blenlib/BLI_index_mask.hh2
-rw-r--r--source/blender/blenlib/BLI_iterator.h10
-rw-r--r--source/blender/blenlib/BLI_linear_allocator.hh18
-rw-r--r--source/blender/blenlib/BLI_map.hh195
-rw-r--r--source/blender/blenlib/BLI_map_slots.hh12
-rw-r--r--source/blender/blenlib/BLI_math_base.h3
-rw-r--r--source/blender/blenlib/BLI_math_color.h5
-rw-r--r--source/blender/blenlib/BLI_math_geom.h10
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h5
-rw-r--r--source/blender/blenlib/BLI_math_solvers.h2
-rw-r--r--source/blender/blenlib/BLI_math_vector.h8
-rw-r--r--source/blender/blenlib/BLI_memarena.h3
-rw-r--r--source/blender/blenlib/BLI_memiter.h21
-rw-r--r--source/blender/blenlib/BLI_mempool.h16
-rw-r--r--source/blender/blenlib/BLI_mesh_intersect.hh61
-rw-r--r--source/blender/blenlib/BLI_mpq3.hh16
-rw-r--r--source/blender/blenlib/BLI_multi_value_map.hh6
-rw-r--r--source/blender/blenlib/BLI_resource_scope.hh (renamed from source/blender/blenlib/BLI_resource_collector.hh)62
-rw-r--r--source/blender/blenlib/BLI_span.hh14
-rw-r--r--source/blender/blenlib/BLI_stack.hh5
-rw-r--r--source/blender/blenlib/BLI_string.h5
-rw-r--r--source/blender/blenlib/BLI_task.h59
-rw-r--r--source/blender/blenlib/BLI_vector.hh48
-rw-r--r--source/blender/blenlib/BLI_vector_set.hh77
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh427
-rw-r--r--source/blender/blenlib/CMakeLists.txt9
-rw-r--r--source/blender/blenlib/intern/BLI_color.cc55
-rw-r--r--source/blender/blenlib/intern/BLI_dial_2d.c2
-rw-r--r--source/blender/blenlib/intern/BLI_mempool.c105
-rw-r--r--source/blender/blenlib/intern/BLI_mempool_private.h52
-rw-r--r--source/blender/blenlib/intern/array_utils.c4
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.cc3
-rw-r--r--source/blender/blenlib/intern/fileops.c4
-rw-r--r--source/blender/blenlib/intern/freetypefont.c1
-rw-r--r--source/blender/blenlib/intern/list_sort_impl.h6
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c16
-rw-r--r--source/blender/blenlib/intern/math_color.c72
-rw-r--r--source/blender/blenlib/intern/math_geom.c4
-rw-r--r--source/blender/blenlib/intern/math_matrix.c151
-rw-r--r--source/blender/blenlib/intern/math_vector.c2
-rw-r--r--source/blender/blenlib/intern/memory_utils.c2
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc413
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc703
-rw-r--r--source/blender/blenlib/intern/noise.c6
-rw-r--r--source/blender/blenlib/intern/path_util.c16
-rw-r--r--source/blender/blenlib/intern/polyfill_2d_beautify.c2
-rw-r--r--source/blender/blenlib/intern/storage.c18
-rw-r--r--source/blender/blenlib/intern/string.c23
-rw-r--r--source/blender/blenlib/intern/task_iterator.c136
-rw-r--r--source/blender/blenlib/intern/task_pool.cc85
-rw-r--r--source/blender/blenlib/intern/timecode.c4
-rw-r--r--source/blender/blenlib/intern/uvproject.c2
-rw-r--r--source/blender/blenlib/intern/winstuff.c4
-rw-r--r--source/blender/blenlib/tests/BLI_color_test.cc133
-rw-r--r--source/blender/blenlib/tests/BLI_function_ref_test.cc25
-rw-r--r--source/blender/blenlib/tests/BLI_linear_allocator_test.cc13
-rw-r--r--source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc2
-rw-r--r--source/blender/blenlib/tests/BLI_map_test.cc49
-rw-r--r--source/blender/blenlib/tests/BLI_stack_cxx_test.cc9
-rw-r--r--source/blender/blenlib/tests/BLI_task_test.cc103
-rw-r--r--source/blender/blenlib/tests/BLI_vector_set_test.cc39
-rw-r--r--source/blender/blenlib/tests/BLI_vector_test.cc9
-rw-r--r--source/blender/blenlib/tests/BLI_virtual_array_test.cc133
-rw-r--r--source/blender/blenloader/BLO_readfile.h5
-rw-r--r--source/blender/blenloader/CMakeLists.txt1
-rw-r--r--source/blender/blenloader/intern/readfile.c24
-rw-r--r--source/blender/blenloader/intern/readfile.h2
-rw-r--r--source/blender/blenloader/intern/readfile_tempload.c21
-rw-r--r--source/blender/blenloader/intern/versioning_250.c3
-rw-r--r--source/blender/blenloader/intern/versioning_280.c14
-rw-r--r--source/blender/blenloader/intern/versioning_290.c174
-rw-r--r--source/blender/blenloader/intern/versioning_300.c263
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c11
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c43
-rw-r--r--source/blender/blenloader/intern/writefile.c1
-rw-r--r--source/blender/blenloader/tests/blendfile_loading_base_test.cc4
-rw-r--r--source/blender/blenloader/tests/blendfile_loading_base_test.h2
-rw-r--r--source/blender/bmesh/CMakeLists.txt14
-rw-r--r--source/blender/bmesh/bmesh.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_core.c26
-rw-r--r--source/blender/bmesh/intern/bmesh_core.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators_inline.h8
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c1659
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h38
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_normals.c1859
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_normals.h62
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_partial_update.c254
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_partial_update.h64
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_tessellate.c457
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_tessellate.h30
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.c28
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.h6
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.c4
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.c289
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.h4
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c22
-rw-r--r--source/blender/bmesh/operators/bmo_connect_pair.c10
-rw-r--r--source/blender/bmesh/operators/bmo_dissolve.c22
-rw-r--r--source/blender/bmesh/operators/bmo_offset_edgeloops.c2
-rw-r--r--source/blender/bmesh/operators/bmo_primitive.c29
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_bisect_plane.c188
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.cc3
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_dissolve.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c2
-rw-r--r--source/blender/bmesh/tools/bmesh_intersect.c6
-rw-r--r--source/blender/bmesh/tools/bmesh_intersect.h8
-rw-r--r--source/blender/compositor/CMakeLists.txt43
-rw-r--r--source/blender/compositor/COM_compositor.h6
-rw-r--r--source/blender/compositor/COM_defines.h62
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.cc65
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.h37
-rw-r--r--source/blender/compositor/intern/COM_CPUDevice.cc33
-rw-r--r--source/blender/compositor/intern/COM_CPUDevice.h4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrder.cc4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrder.h4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrderHotspot.cc4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrderHotspot.h4
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.cc31
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h24
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc36
-rw-r--r--source/blender/compositor/intern/COM_Converter.h7
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc185
-rw-r--r--source/blender/compositor/intern/COM_Debug.h105
-rw-r--r--source/blender/compositor/intern/COM_Device.h12
-rw-r--r--source/blender/compositor/intern/COM_Enums.cc61
-rw-r--r--source/blender/compositor/intern/COM_Enums.h91
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.cc217
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.h159
-rw-r--r--source/blender/compositor/intern/COM_ExecutionModel.cc48
-rw-r--r--source/blender/compositor/intern/COM_ExecutionModel.h84
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.cc155
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.h49
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc362
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.h88
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc104
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h159
-rw-r--r--source/blender/compositor/intern/COM_MemoryProxy.cc8
-rw-r--r--source/blender/compositor/intern/COM_MemoryProxy.h19
-rw-r--r--source/blender/compositor/intern/COM_MetaData.cc6
-rw-r--r--source/blender/compositor/intern/COM_MetaData.h6
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.cc26
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.h73
-rw-r--r--source/blender/compositor/intern/COM_Node.cc24
-rw-r--r--source/blender/compositor/intern/COM_Node.h73
-rw-r--r--source/blender/compositor/intern/COM_NodeConverter.cc4
-rw-r--r--source/blender/compositor/intern/COM_NodeConverter.h4
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.cc46
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.h21
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.cc343
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h622
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc119
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.h30
-rw-r--r--source/blender/compositor/intern/COM_OpenCLDevice.cc26
-rw-r--r--source/blender/compositor/intern/COM_OpenCLDevice.h7
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.cc129
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.h71
-rw-r--r--source/blender/compositor/intern/COM_SingleThreadedOperation.cc7
-rw-r--r--source/blender/compositor/intern/COM_SingleThreadedOperation.h9
-rw-r--r--source/blender/compositor/intern/COM_SocketReader.h153
-rw-r--r--source/blender/compositor/intern/COM_TiledExecutionModel.cc158
-rw-r--r--source/blender/compositor/intern/COM_TiledExecutionModel.h54
-rw-r--r--source/blender/compositor/intern/COM_WorkPackage.cc18
-rw-r--r--source/blender/compositor/intern/COM_WorkPackage.h38
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.cc42
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.h12
-rw-r--r--source/blender/compositor/intern/COM_compositor.cc16
-rw-r--r--source/blender/compositor/nodes/COM_AlphaOverNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_AlphaOverNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_AntiAliasingNode.cc60
-rw-r--r--source/blender/compositor/nodes/COM_AntiAliasingNode.h40
-rw-r--r--source/blender/compositor/nodes/COM_BilateralBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BilateralBlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BlurNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_BlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BokehBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BokehBlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BokehImageNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BokehImageNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BoxMaskNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_BoxMaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BrightnessNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BrightnessNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ChannelMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ChannelMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ChromaMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ChromaMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorBalanceNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorBalanceNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorCorrectionNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorCorrectionNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorCurveNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorCurveNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorExposureNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorExposureNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorRampNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorRampNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorSpillNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorSpillNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorToBWNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorToBWNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CompositorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CompositorNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_ConvertAlphaNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ConvertAlphaNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CornerPinNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CornerPinNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CropNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CropNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.cc41
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.h22
-rw-r--r--source/blender/compositor/nodes/COM_DefocusNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_DefocusNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DenoiseNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DenoiseNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DespeckleNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DespeckleNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DifferenceMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DifferenceMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DilateErodeNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_DilateErodeNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DirectionalBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DirectionalBlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DisplaceNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_DisplaceNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DistanceMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DistanceMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_EllipseMaskNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_EllipseMaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_FilterNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_FilterNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_FlipNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_FlipNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_GammaNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_GammaNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_GlareNode.cc8
-rw-r--r--source/blender/compositor/nodes/COM_GlareNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_IDMaskNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_IDMaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ImageNode.cc22
-rw-r--r--source/blender/compositor/nodes/COM_ImageNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_InpaintNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_InpaintNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_InvertNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_InvertNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_KeyingNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_KeyingNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_KeyingScreenNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_KeyingScreenNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_LensDistortionNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_LensDistortionNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_LuminanceMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_LuminanceMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MapRangeNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MapRangeNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_MapUVNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MapUVNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MapValueNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MapValueNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_MaskNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MathNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MathNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MixNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MixNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_MovieClipNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MovieClipNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MovieDistortionNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MovieDistortionNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_NormalNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_NormalNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_NormalizeNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_NormalizeNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_OutputFileNode.cc56
-rw-r--r--source/blender/compositor/nodes/COM_OutputFileNode.h12
-rw-r--r--source/blender/compositor/nodes/COM_PixelateNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_PixelateNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_RenderLayersNode.cc13
-rw-r--r--source/blender/compositor/nodes/COM_RenderLayersNode.h3
-rw-r--r--source/blender/compositor/nodes/COM_RotateNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_RotateNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SetAlphaNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SetAlphaNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SocketProxyNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SocketProxyNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SplitViewerNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SplitViewerNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SunBeamsNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SunBeamsNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SwitchNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SwitchNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_SwitchViewNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SwitchViewNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_TextureNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TextureNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TimeNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TimeNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TonemapNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TonemapNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TrackPositionNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TrackPositionNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TranslateNode.cc9
-rw-r--r--source/blender/compositor/nodes/COM_TranslateNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ValueNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ValueNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_VectorBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_VectorBlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_VectorCurveNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_VectorCurveNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ViewLevelsNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ViewLevelsNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ViewerNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ViewerNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_ZCombineNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ZCombineNode.h4
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverKeyOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverMixedOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_AntiAliasOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_AntiAliasOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BilateralBlurOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_BilateralBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.cc27
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BoxMaskOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_BoxMaskOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BrightnessOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_BrightnessOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CalculateMeanOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_CalculateMeanOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h7
-rw-r--r--source/blender/compositor/operations/COM_ChangeHSVOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ChangeHSVOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ChannelMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ChannelMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ChromaMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ChromaMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorCorrectionOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorCorrectionOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorCurveOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorCurveOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorExposureOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorExposureOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorRampOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorRampOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorSpillOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorSpillOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.cc18
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ConvertColorProfileOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h5
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.cc39
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h5
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionFilterOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_CurveBaseOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_CurveBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DenoiseOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_DenoiseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DespeckleOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_DespeckleOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DifferenceMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DifferenceMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DilateErodeOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_DilateErodeOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DirectionalBlurOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_DirectionalBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.cc14
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_DotproductOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DotproductOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GammaCorrectOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GammaCorrectOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GammaOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GammaOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GaussianXBlurOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GaussianXBlurOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_GaussianYBlurOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GaussianYBlurOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_GlareBaseOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GlareBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareFogGlowOperation.cc21
-rw-r--r--source/blender/compositor/operations/COM_GlareFogGlowOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareGhostOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_GlareGhostOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GlareSimpleStarOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareStreaksOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GlareStreaksOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareThresholdOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_GlareThresholdOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_IDMaskOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_IDMaskOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_InpaintOperation.cc9
-rw-r--r--source/blender/compositor/operations/COM_InpaintOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_InvertOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_InvertOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingBlurOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_KeyingBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingClipOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_KeyingClipOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingDespillOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_KeyingDespillOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_LuminanceMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_LuminanceMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MapRangeOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MapRangeOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MapValueOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MapValueOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MaskOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MaskOperation.h7
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.cc126
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.h23
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MultilayerImageOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_MultilayerImageOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_NormalizeOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_NormalizeOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.cc18
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.h26
-rw-r--r--source/blender/compositor/operations/COM_PixelateOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_PixelateOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc76
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h62
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.cc57
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_PreviewOperation.cc14
-rw-r--r--source/blender/compositor/operations/COM_PreviewOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_QualityStepHelper.cc18
-rw-r--r--source/blender/compositor/operations/COM_QualityStepHelper.h10
-rw-r--r--source/blender/compositor/operations/COM_ReadBufferOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_ReadBufferOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.cc12
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.h6
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.cc868
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.h149
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SetColorOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_SetColorOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_SetSamplerOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_SetSamplerOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SetValueOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_SetValueOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_SetVectorOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_SetVectorOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_SocketProxyOperation.cc9
-rw-r--r--source/blender/compositor/operations/COM_SocketProxyOperation.h26
-rw-r--r--source/blender/compositor/operations/COM_SplitOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_SplitOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SunBeamsOperation.cc14
-rw-r--r--source/blender/compositor/operations/COM_SunBeamsOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.cc40
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_TrackPositionOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_TrackPositionOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_TranslateOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_TranslateOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc22
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_VectorBlurOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_VectorBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_VectorCurveOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_VectorCurveOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.cc18
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_WrapOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_WrapOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_WriteBufferOperation.cc9
-rw-r--r--source/blender/compositor/operations/COM_WriteBufferOperation.h13
-rw-r--r--source/blender/compositor/operations/COM_ZCombineOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ZCombineOperation.h4
-rw-r--r--source/blender/datatoc/datatoc_icon.c49
-rw-r--r--source/blender/depsgraph/DEG_depsgraph.h21
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h9
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.h1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc113
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h5
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc39
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline.h2
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc7
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h3
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc31
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc23
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc33
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc3
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc35
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc8
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h3
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc4
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h3
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.cc1
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.h3
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.cc4
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h1
-rw-r--r--source/blender/draw/CMakeLists.txt18
-rw-r--r--source/blender/draw/DRW_engine.h3
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c10
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c3
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c20
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c6
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c84
-rw-r--r--source/blender/draw/engines/eevee/eevee_mist.c17
-rw-r--r--source/blender/draw/engines/eevee/eevee_occlusion.c17
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_screen_raytrace.c18
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c18
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c22
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl6
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl93
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl60
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c11
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c24
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c5
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h4
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader_fx.c10
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl4
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl4
-rw-r--r--source/blender/draw/engines/image/image_engine.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.c31
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_mesh.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c12
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c7
-rw-r--r--source/blender/draw/engines/overlay/overlay_gpencil.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_mode_transfer.c159
-rw-r--r--source/blender/draw/engines/overlay/overlay_motion_path.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_paint.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_particle.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.h12
-rw-r--r--source/blender/draw/engines/overlay/overlay_wireframe.c30
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_volume.c4
-rw-r--r--source/blender/draw/intern/DRW_render.h7
-rw-r--r--source/blender/draw/intern/draw_cache.h3
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h50
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c6165
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc813
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_extractors.c3687
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_private.h295
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_render_data.c371
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h7
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.cc (renamed from source/blender/draw/intern/draw_cache_impl_curve.c)242
-rw-r--r--source/blender/draw/intern/draw_cache_impl_displist.c10
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c11
-rw-r--r--source/blender/draw/intern/draw_cache_impl_hair.c3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c78
-rw-r--r--source/blender/draw/intern/draw_cache_impl_volume.c12
-rw-r--r--source/blender/draw/intern/draw_cache_inline.h8
-rw-r--r--source/blender/draw/intern/draw_hair.c200
-rw-r--r--source/blender/draw/intern/draw_manager.c17
-rw-r--r--source/blender/draw/intern/draw_manager.h13
-rw-r--r--source/blender/draw/intern/draw_manager_data.c36
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c10
-rw-r--r--source/blender/draw/intern/draw_manager_profiling.c6
-rw-r--r--source/blender/draw/intern/draw_manager_shader.c7
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc392
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc119
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc256
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc198
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc127
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc182
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc269
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl78
-rw-r--r--source/blender/draw/intern/shaders/common_hair_refine_comp.glsl24
-rw-r--r--source/blender/draw/intern/shaders/common_hair_refine_vert.glsl45
-rw-r--r--source/blender/draw/intern/shaders/common_math_lib.glsl7
-rw-r--r--source/blender/draw/intern/shaders/common_pointcloud_lib.glsl2
-rw-r--r--source/blender/draw/tests/draw_testing.cc18
-rw-r--r--source/blender/draw/tests/draw_testing.hh13
-rw-r--r--source/blender/draw/tests/shaders_test.cc12
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c5
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c10
-rw-r--r--source/blender/editors/animation/keyframing.c13
-rw-r--r--source/blender/editors/armature/CMakeLists.txt1
-rw-r--r--source/blender/editors/armature/armature_add.c14
-rw-r--r--source/blender/editors/armature/armature_naming.c4
-rw-r--r--source/blender/editors/armature/armature_select.c206
-rw-r--r--source/blender/editors/armature/pose_edit.c4
-rw-r--r--source/blender/editors/armature/pose_lib.c11
-rw-r--r--source/blender/editors/armature/pose_select.c188
-rw-r--r--source/blender/editors/armature/pose_slide.c1224
-rw-r--r--source/blender/editors/armature/pose_transform.c13
-rw-r--r--source/blender/editors/armature/pose_utils.c5
-rw-r--r--source/blender/editors/curve/curve_intern.h5
-rw-r--r--source/blender/editors/curve/editcurve.c25
-rw-r--r--source/blender/editors/curve/editcurve_add.c12
-rw-r--r--source/blender/editors/curve/editcurve_paint.c18
-rw-r--r--source/blender/editors/curve/editcurve_select.c2
-rw-r--r--source/blender/editors/curve/editfont.c3
-rw-r--r--source/blender/editors/geometry/geometry_attributes.c10
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c40
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c484
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt2
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_add_blank.c101
-rw-r--r--source/blender/editors/gpencil/gpencil_add_lineart.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.c448
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c360
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c441
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c22
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h7
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c77
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c56
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c44
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_ops.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_utils.c10
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c40
-rw-r--r--source/blender/editors/include/ED_armature.h14
-rw-r--r--source/blender/editors/include/ED_fileselect.h2
-rw-r--r--source/blender/editors/include/ED_gizmo_library.h21
-rw-r--r--source/blender/editors/include/ED_gpencil.h3
-rw-r--r--source/blender/editors/include/ED_keyframing.h1
-rw-r--r--source/blender/editors/include/ED_mesh.h13
-rw-r--r--source/blender/editors/include/ED_numinput.h5
-rw-r--r--source/blender/editors/include/ED_object.h18
-rw-r--r--source/blender/editors/include/ED_render.h3
-rw-r--r--source/blender/editors/include/ED_screen.h9
-rw-r--r--source/blender/editors/include/ED_sequencer.h1
-rw-r--r--source/blender/editors/include/ED_spreadsheet.h43
-rw-r--r--source/blender/editors/include/ED_text.h2
-rw-r--r--source/blender/editors/include/ED_transform.h6
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h17
-rw-r--r--source/blender/editors/include/ED_uvedit.h1
-rw-r--r--source/blender/editors/include/ED_view3d.h43
-rw-r--r--source/blender/editors/include/UI_interface.h31
-rw-r--r--source/blender/editors/include/UI_resources.h2
-rw-r--r--source/blender/editors/interface/interface.c57
-rw-r--r--source/blender/editors/interface/interface_context_menu.c10
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c11
-rw-r--r--source/blender/editors/interface/interface_eyedropper_color.c105
-rw-r--r--source/blender/editors/interface/interface_handlers.c355
-rw-r--r--source/blender/editors/interface/interface_icons.c14
-rw-r--r--source/blender/editors/interface/interface_intern.h100
-rw-r--r--source/blender/editors/interface/interface_layout.c8
-rw-r--r--source/blender/editors/interface/interface_ops.c224
-rw-r--r--source/blender/editors/interface/interface_panel.c22
-rw-r--r--source/blender/editors/interface/interface_region_menu_popup.c13
-rw-r--r--source/blender/editors/interface/interface_region_search.c3
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c25
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c12
-rw-r--r--source/blender/editors/interface/interface_template_search_operator.c1
-rw-r--r--source/blender/editors/interface/interface_templates.c14
-rw-r--r--source/blender/editors/interface/interface_widgets.c101
-rw-r--r--source/blender/editors/interface/view2d_ops.c2
-rw-r--r--source/blender/editors/io/CMakeLists.txt2
-rw-r--r--source/blender/editors/io/io_alembic.c27
-rw-r--r--source/blender/editors/io/io_collada.c10
-rw-r--r--source/blender/editors/io/io_gpencil.h1
-rw-r--r--source/blender/editors/io/io_gpencil_export.c14
-rw-r--r--source/blender/editors/io/io_gpencil_import.c5
-rw-r--r--source/blender/editors/mask/mask_add.c40
-rw-r--r--source/blender/editors/mask/mask_shapekey.c4
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c48
-rw-r--r--source/blender/editors/mesh/editmesh_bisect.c20
-rw-r--r--source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c1
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c28
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c2116
-rw-r--r--source/blender/editors/mesh/editmesh_path.c5
-rw-r--r--source/blender/editors/mesh/editmesh_select.c173
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c4
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c31
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c104
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c6
-rw-r--r--source/blender/editors/mesh/meshtools.c2
-rw-r--r--source/blender/editors/object/CMakeLists.txt2
-rw-r--r--source/blender/editors/object/object_add.c409
-rw-r--r--source/blender/editors/object/object_bake_api.c129
-rw-r--r--source/blender/editors/object/object_data_transfer.c3
-rw-r--r--source/blender/editors/object/object_data_transform.c9
-rw-r--r--source/blender/editors/object/object_edit.c142
-rw-r--r--source/blender/editors/object/object_intern.h6
-rw-r--r--source/blender/editors/object/object_modes.c220
-rw-r--r--source/blender/editors/object/object_modifier.c158
-rw-r--r--source/blender/editors/object/object_ops.c2
-rw-r--r--source/blender/editors/object/object_relations.c15
-rw-r--r--source/blender/editors/object/object_remesh.c4
-rw-r--r--source/blender/editors/object/object_transform.c25
-rw-r--r--source/blender/editors/object/object_vgroup.c6
-rw-r--r--source/blender/editors/physics/particle_edit.c27
-rw-r--r--source/blender/editors/physics/rigidbody_object.c23
-rw-r--r--source/blender/editors/render/render_internal.c7
-rw-r--r--source/blender/editors/render/render_opengl.c2
-rw-r--r--source/blender/editors/render/render_preview.c45
-rw-r--r--source/blender/editors/render/render_update.c2
-rw-r--r--source/blender/editors/render/render_view.c1
-rw-r--r--source/blender/editors/screen/area.c25
-rw-r--r--source/blender/editors/screen/screen_draw.c313
-rw-r--r--source/blender/editors/screen/screen_edit.c246
-rw-r--r--source/blender/editors/screen/screen_geometry.c28
-rw-r--r--source/blender/editors/screen/screen_intern.h47
-rw-r--r--source/blender/editors/screen/screen_ops.c388
-rw-r--r--source/blender/editors/screen/screendump.c3
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c41
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h4
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c21
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c9
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c64
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c19
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c7
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_expand.c49
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c4
-rw-r--r--source/blender/editors/sound/sound_ops.c3
-rw-r--r--source/blender/editors/space_action/action_edit.c14
-rw-r--r--source/blender/editors/space_action/space_action.c5
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c14
-rw-r--r--source/blender/editors/space_buttons/buttons_intern.h2
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c50
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c6
-rw-r--r--source/blender/editors/space_clip/clip_editor.c2
-rw-r--r--source/blender/editors/space_clip/clip_ops.c10
-rw-r--r--source/blender/editors/space_clip/tracking_ops_track.c26
-rw-r--r--source/blender/editors/space_file/file_draw.c39
-rw-r--r--source/blender/editors/space_file/file_intern.h1
-rw-r--r--source/blender/editors/space_file/file_ops.c129
-rw-r--r--source/blender/editors/space_file/filelist.c94
-rw-r--r--source/blender/editors/space_file/filesel.c1
-rw-r--r--source/blender/editors/space_file/space_file.c10
-rw-r--r--source/blender/editors/space_graph/graph_edit.c14
-rw-r--r--source/blender/editors/space_graph/graph_select.c190
-rw-r--r--source/blender/editors/space_image/image_buttons.c5
-rw-r--r--source/blender/editors/space_image/image_draw.c2
-rw-r--r--source/blender/editors/space_image/image_intern.h1
-rw-r--r--source/blender/editors/space_image/image_ops.c221
-rw-r--r--source/blender/editors/space_image/space_image.c1
-rw-r--r--source/blender/editors/space_info/info_draw.c4
-rw-r--r--source/blender/editors/space_info/info_ops.c90
-rw-r--r--source/blender/editors/space_info/info_stats.c127
-rw-r--r--source/blender/editors/space_info/space_info.c9
-rw-r--r--source/blender/editors/space_nla/nla_draw.c8
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt18
-rw-r--r--source/blender/editors/space_node/drawnode.cc (renamed from source/blender/editors/space_node/drawnode.c)976
-rw-r--r--source/blender/editors/space_node/node_add.cc (renamed from source/blender/editors/space_node/node_add.c)95
-rw-r--r--source/blender/editors/space_node/node_buttons.c5
-rw-r--r--source/blender/editors/space_node/node_draw.cc64
-rw-r--r--source/blender/editors/space_node/node_edit.cc (renamed from source/blender/editors/space_node/node_edit.c)278
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc130
-rw-r--r--source/blender/editors/space_node/node_gizmo.c1
-rw-r--r--source/blender/editors/space_node/node_group.cc (renamed from source/blender/editors/space_node/node_group.c)91
-rw-r--r--source/blender/editors/space_node/node_intern.h10
-rw-r--r--source/blender/editors/space_node/node_relationships.cc (renamed from source/blender/editors/space_node/node_relationships.c)435
-rw-r--r--source/blender/editors/space_node/node_select.cc (renamed from source/blender/editors/space_node/node_select.c)166
-rw-r--r--source/blender/editors/space_node/node_templates.cc (renamed from source/blender/editors/space_node/node_templates.c)124
-rw-r--r--source/blender/editors/space_node/node_toolbar.cc (renamed from source/blender/editors/space_node/node_toolbar.c)0
-rw-r--r--source/blender/editors/space_node/node_view.cc (renamed from source/blender/editors/space_node/node_view.c)68
-rw-r--r--source/blender/editors/space_node/space_node.c17
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c13
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c107
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c16
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h2
-rw-r--r--source/blender/editors/space_outliner/outliner_sync.c7
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c33
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c9
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.c1
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c4
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.cc3
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.hh18
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library.cc204
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.cc41
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.hh1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c91
-rw-r--r--source/blender/editors/space_sequencer/sequencer_buttons.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c95
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c194
-rw-r--r--source/blender/editors/space_sequencer/sequencer_modifier.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_proxy.c14
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c150
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt18
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc251
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh58
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.cc72
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.hh48
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc230
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh115
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh92
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.cc306
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.hh (renamed from source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh)15
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc24
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh65
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc445
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh94
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.cc18
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.hh1
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc447
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc256
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.hh42
-rw-r--r--source/blender/editors/space_text/space_text.c13
-rw-r--r--source/blender/editors/space_text/text_draw.c4
-rw-r--r--source/blender/editors/space_text/text_intern.h1
-rw-r--r--source/blender/editors/space_text/text_ops.c10
-rw-r--r--source/blender/editors/space_userpref/space_userpref.c3
-rw-r--r--source/blender/editors/space_view3d/drawobject.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c16
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c202
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c16
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_navigate.c19
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c15
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h11
-rw-r--r--source/blender/editors/space_view3d/view3d_ops.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c42
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c15
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c259
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c106
-rw-r--r--source/blender/editors/space_view3d/view3d_walk.c2
-rw-r--r--source/blender/editors/transform/transform.c179
-rw-r--r--source/blender/editors/transform/transform.h64
-rw-r--r--source/blender/editors/transform/transform_constraints.c16
-rw-r--r--source/blender/editors/transform/transform_convert.c244
-rw-r--r--source/blender/editors/transform/transform_convert.h8
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c14
-rw-r--r--source/blender/editors/transform/transform_convert_cursor.c26
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c14
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c10
-rw-r--r--source/blender/editors/transform/transform_convert_lattice.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mball.c20
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c1330
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_edge.c7
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_skin.c30
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c2
-rw-r--r--source/blender/editors/transform/transform_convert_object.c12
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c25
-rw-r--r--source/blender/editors/transform/transform_generics.c82
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c50
-rw-r--r--source/blender/editors/transform/transform_gizmo_extrude_3d.c15
-rw-r--r--source/blender/editors/transform/transform_input.c55
-rw-r--r--source/blender/editors/transform/transform_mode.c137
-rw-r--r--source/blender/editors/transform/transform_mode.h5
-rw-r--r--source/blender/editors/transform/transform_mode_curveshrinkfatten.c1
-rw-r--r--source/blender/editors/transform/transform_mode_edge_rotate_normal.c4
-rw-r--r--source/blender/editors/transform/transform_mode_edge_seq_slide.c10
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c8
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c1
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c1
-rw-r--r--source/blender/editors/transform/transform_mode_maskshrinkfatten.c3
-rw-r--r--source/blender/editors/transform/transform_mode_mirror.c3
-rw-r--r--source/blender/editors/transform/transform_mode_resize.c7
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c4
-rw-r--r--source/blender/editors/transform/transform_mode_shear.c2
-rw-r--r--source/blender/editors/transform/transform_mode_shrink_fatten.c6
-rw-r--r--source/blender/editors/transform/transform_mode_skin_resize.c3
-rw-r--r--source/blender/editors/transform/transform_mode_timetranslate.c4
-rw-r--r--source/blender/editors/transform/transform_mode_trackball.c26
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c107
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c8
-rw-r--r--source/blender/editors/transform/transform_orientations.c61
-rw-r--r--source/blender/editors/transform/transform_orientations.h2
-rw-r--r--source/blender/editors/transform/transform_snap.c8
-rw-r--r--source/blender/editors/transform/transform_snap_object.c685
-rw-r--r--source/blender/editors/undo/ed_undo.c24
-rw-r--r--source/blender/editors/util/CMakeLists.txt3
-rw-r--r--source/blender/editors/util/ed_draw.c2
-rw-r--r--source/blender/editors/util/ed_transverts.c6
-rw-r--r--source/blender/editors/util/ed_util.c24
-rw-r--r--source/blender/editors/util/ed_util_ops.cc2
-rw-r--r--source/blender/editors/util/numinput.c25
-rw-r--r--source/blender/editors/util/select_utils.c6
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c24
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.h7
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c3
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c79
-rw-r--r--source/blender/freestyle/intern/geometry/Bezier.cpp8
-rw-r--r--source/blender/freestyle/intern/geometry/Bezier.h1
-rw-r--r--source/blender/freestyle/intern/geometry/FitCurve.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/FitCurve.h1
-rw-r--r--source/blender/freestyle/intern/geometry/GridHelpers.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/normal_cycle.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/normal_cycle.h1
-rw-r--r--source/blender/freestyle/intern/scene_graph/NodeCamera.cpp11
-rw-r--r--source/blender/freestyle/intern/scene_graph/NodeCamera.h2
-rw-r--r--source/blender/freestyle/intern/stroke/ChainingIterators.h2
-rw-r--r--source/blender/freestyle/intern/stroke/Curve.h4
-rw-r--r--source/blender/freestyle/intern/stroke/Stroke.cpp4
-rw-r--r--source/blender/freestyle/intern/stroke/Stroke.h3
-rw-r--r--source/blender/freestyle/intern/stroke/StrokeRenderer.cpp8
-rw-r--r--source/blender/freestyle/intern/stroke/StrokeRenderer.h1
-rw-r--r--source/blender/freestyle/intern/system/PseudoNoise.cpp4
-rw-r--r--source/blender/freestyle/intern/system/PseudoNoise.h2
-rw-r--r--source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp8
-rw-r--r--source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h2
-rw-r--r--source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp8
-rw-r--r--source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h2
-rw-r--r--source/blender/freestyle/intern/view_map/BoxGrid.cpp20
-rw-r--r--source/blender/freestyle/intern/view_map/BoxGrid.h6
-rw-r--r--source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/CulledOccluderSource.h1
-rw-r--r--source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h1
-rw-r--r--source/blender/freestyle/intern/view_map/OccluderSource.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp8
-rw-r--r--source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h2
-rw-r--r--source/blender/freestyle/intern/view_map/SphericalGrid.cpp20
-rw-r--r--source/blender/freestyle/intern/view_map/SphericalGrid.h6
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp4
-rw-r--r--source/blender/functions/FN_cpp_type.hh11
-rw-r--r--source/blender/functions/FN_generic_pointer.hh10
-rw-r--r--source/blender/functions/FN_generic_span.hh20
-rw-r--r--source/blender/functions/FN_generic_value_map.hh5
-rw-r--r--source/blender/functions/FN_generic_vector_array.hh6
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh729
-rw-r--r--source/blender/functions/FN_generic_virtual_vector_array.hh16
-rw-r--r--source/blender/functions/FN_multi_function_network_optimization.hh4
-rw-r--r--source/blender/functions/FN_multi_function_params.hh25
-rw-r--r--source/blender/functions/intern/cpp_types.cc4
-rw-r--r--source/blender/functions/intern/generic_vector_array.cc6
-rw-r--r--source/blender/functions/intern/generic_virtual_array.cc309
-rw-r--r--source/blender/functions/intern/generic_virtual_vector_array.cc26
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc109
-rw-r--r--source/blender/functions/intern/multi_function_network_optimization.cc27
-rw-r--r--source/blender/functions/tests/FN_multi_function_network_test.cc2
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt10
-rw-r--r--source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c14
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c19
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillength.c223
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c63
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c66
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c73
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c61
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h155
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c590
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c1736
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h50
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_util.c49
-rw-r--r--source/blender/gpu/CMakeLists.txt12
-rw-r--r--source/blender/gpu/GPU_capabilities.h13
-rw-r--r--source/blender/gpu/GPU_common.h1
-rw-r--r--source/blender/gpu/GPU_compute.h (renamed from source/blender/blenkernel/BKE_mesh_boolean_convert.h)24
-rw-r--r--source/blender/gpu/GPU_framebuffer.h5
-rw-r--r--source/blender/gpu/GPU_index_buffer.h21
-rw-r--r--source/blender/gpu/GPU_matrix.h36
-rw-r--r--source/blender/gpu/GPU_platform.h3
-rw-r--r--source/blender/gpu/GPU_shader.h7
-rw-r--r--source/blender/gpu/GPU_state.h1
-rw-r--r--source/blender/gpu/GPU_texture.h6
-rw-r--r--source/blender/gpu/GPU_vertex_buffer.h10
-rw-r--r--source/blender/gpu/intern/gpu_backend.hh1
-rw-r--r--source/blender/gpu/intern/gpu_batch_private.hh3
-rw-r--r--source/blender/gpu/intern/gpu_capabilities.cc60
-rw-r--r--source/blender/gpu/intern/gpu_capabilities_private.hh13
-rw-r--r--source/blender/gpu/intern/gpu_compute.cc41
-rw-r--r--source/blender/gpu/intern/gpu_context.cc2
-rw-r--r--source/blender/gpu/intern/gpu_debug.cc4
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc19
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer_private.hh14
-rw-r--r--source/blender/gpu/intern/gpu_index_buffer.cc93
-rw-r--r--source/blender/gpu/intern/gpu_index_buffer_private.hh14
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c7
-rw-r--r--source/blender/gpu/intern/gpu_matrix.cc49
-rw-r--r--source/blender/gpu/intern/gpu_platform.cc73
-rw-r--r--source/blender/gpu/intern/gpu_platform_private.hh16
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc85
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.cc15
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.hh6
-rw-r--r--source/blender/gpu/intern/gpu_shader_private.hh1
-rw-r--r--source/blender/gpu/intern/gpu_state.cc6
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc19
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh7
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.cc15
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer_private.hh3
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc107
-rw-r--r--source/blender/gpu/opengl/gl_backend.hh7
-rw-r--r--source/blender/gpu/opengl/gl_batch.cc16
-rw-r--r--source/blender/gpu/opengl/gl_batch.hh5
-rw-r--r--source/blender/gpu/opengl/gl_compute.cc35
-rw-r--r--source/blender/gpu/opengl/gl_compute.hh30
-rw-r--r--source/blender/gpu/opengl/gl_drawlist.cc1
-rw-r--r--source/blender/gpu/opengl/gl_immediate.hh2
-rw-r--r--source/blender/gpu/opengl/gl_index_buffer.cc34
-rw-r--r--source/blender/gpu/opengl/gl_index_buffer.hh7
-rw-r--r--source/blender/gpu/opengl/gl_shader.cc38
-rw-r--r--source/blender/gpu/opengl/gl_shader.hh4
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc45
-rw-r--r--source/blender/gpu/opengl/gl_state.hh3
-rw-r--r--source/blender/gpu/opengl/gl_texture.cc6
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.cc45
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.hh8
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl26
-rw-r--r--source/blender/gpu/shaders/gpu_shader_text_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl5
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl1
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl6
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl15
-rw-r--r--source/blender/gpu/tests/gpu_index_buffer_test.cc47
-rw-r--r--source/blender/gpu/tests/gpu_shader_test.cc301
-rw-r--r--source/blender/gpu/tests/gpu_testing.cc6
-rw-r--r--source/blender/imbuf/CMakeLists.txt6
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h6
-rw-r--r--source/blender/imbuf/IMB_imbuf.h16
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h11
-rw-r--r--source/blender/imbuf/intern/IMB_indexer.h11
-rw-r--r--source/blender/imbuf/intern/anim_movie.c827
-rw-r--r--source/blender/imbuf/intern/bmp.c65
-rw-r--r--source/blender/imbuf/intern/cineon/cineon_dpx.c12
-rw-r--r--source/blender/imbuf/intern/colormanagement.c9
-rw-r--r--source/blender/imbuf/intern/colormanagement_inline.c4
-rw-r--r--source/blender/imbuf/intern/dds/ColorBlock.cpp5
-rw-r--r--source/blender/imbuf/intern/dds/ColorBlock.h2
-rw-r--r--source/blender/imbuf/intern/dds/DirectDrawSurface.cpp4
-rw-r--r--source/blender/imbuf/intern/dds/DirectDrawSurface.h1
-rw-r--r--source/blender/imbuf/intern/divers.c9
-rw-r--r--source/blender/imbuf/intern/imageprocess.c278
-rw-r--r--source/blender/imbuf/intern/indexer.c417
-rw-r--r--source/blender/imbuf/intern/iris.c6
-rw-r--r--source/blender/imbuf/intern/jpeg.c3
-rw-r--r--source/blender/imbuf/intern/metadata.c6
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp4
-rw-r--r--source/blender/imbuf/intern/radiance_hdr.c138
-rw-r--r--source/blender/imbuf/intern/rectop.c9
-rw-r--r--source/blender/imbuf/intern/tiff.c17
-rw-r--r--source/blender/imbuf/intern/util.c18
-rw-r--r--source/blender/io/alembic/ABC_alembic.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.cc4
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.h2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.cc4
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_instance.cc4
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_instance.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mball.cc2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc17
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.h1
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc72
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.h18
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc5
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.h2
-rw-r--r--source/blender/io/collada/DocumentImporter.cpp2
-rw-r--r--source/blender/io/collada/ErrorHandler.cpp5
-rw-r--r--source/blender/io/collada/ErrorHandler.h2
-rw-r--r--source/blender/io/collada/ExtraHandler.cpp4
-rw-r--r--source/blender/io/collada/ExtraHandler.h3
-rw-r--r--source/blender/io/collada/ExtraTags.cpp4
-rw-r--r--source/blender/io/collada/Materials.cpp82
-rw-r--r--source/blender/io/collada/Materials.h3
-rw-r--r--source/blender/io/collada/SkinInfo.cpp5
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h5
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator.cc4
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.cc8
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.hh3
-rw-r--r--source/blender/io/common/intern/object_identifier.cc9
-rw-r--r--source/blender/io/gpencil/CMakeLists.txt31
-rw-r--r--source/blender/io/gpencil/gpencil_io.h2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.cc43
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_base.h)5
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_capi.cc16
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_base.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_export_base.h)2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc46
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_export_pdf.h)4
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_svg.cc9
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_svg.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_export_svg.h)10
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_base.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_base.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_import_base.h)2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.cc20
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_import_svg.h)2
-rw-r--r--source/blender/io/gpencil/nanosvg/nanosvg.h32
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.cc4
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h1
-rw-r--r--source/blender/io/usd/intern/usd_writer_metaball.cc2
-rw-r--r--source/blender/makesdna/DNA_ID.h22
-rw-r--r--source/blender/makesdna/DNA_action_types.h13
-rw-r--r--source/blender/makesdna/DNA_armature_types.h7
-rw-r--r--source/blender/makesdna/DNA_boid_types.h5
-rw-r--r--source/blender/makesdna/DNA_brush_types.h3
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h2
-rw-r--r--source/blender/makesdna/DNA_curve_types.h31
-rw-r--r--source/blender/makesdna/DNA_effect_types.h9
-rw-r--r--source/blender/makesdna/DNA_genfile.h1
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h16
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h65
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h6
-rw-r--r--source/blender/makesdna/DNA_ipo_types.h5
-rw-r--r--source/blender/makesdna/DNA_light_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_light_types.h2
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h3
-rw-r--r--source/blender/makesdna/DNA_mesh_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h8
-rw-r--r--source/blender/makesdna/DNA_modifier_defaults.h4
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h2
-rw-r--r--source/blender/makesdna/DNA_movieclip_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_node_types.h153
-rw-r--r--source/blender/makesdna/DNA_object_force_types.h9
-rw-r--r--source/blender/makesdna/DNA_object_types.h9
-rw-r--r--source/blender/makesdna/DNA_particle_types.h10
-rw-r--r--source/blender/makesdna/DNA_pointcache_types.h3
-rw-r--r--source/blender/makesdna/DNA_scene_types.h6
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h9
-rw-r--r--source/blender/makesdna/DNA_space_types.h88
-rw-r--r--source/blender/makesdna/DNA_texture_types.h3
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h5
-rw-r--r--source/blender/makesdna/DNA_view3d_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h4
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h15
-rw-r--r--source/blender/makesdna/DNA_xr_types.h15
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c2
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c15
-rw-r--r--source/blender/makesdna/intern/dna_utils.c3
-rw-r--r--source/blender/makesdna/intern/makesdna.c5
-rw-r--r--source/blender/makesrna/RNA_access.h6
-rw-r--r--source/blender/makesrna/RNA_define.h3
-rw-r--r--source/blender/makesrna/RNA_enum_types.h1
-rw-r--r--source/blender/makesrna/RNA_types.h58
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesrna/intern/makesrna.c33
-rw-r--r--source/blender/makesrna/intern/rna_ID.c356
-rw-r--r--source/blender/makesrna/intern/rna_access.c134
-rw-r--r--source/blender/makesrna/intern/rna_action.c6
-rw-r--r--source/blender/makesrna/intern/rna_action_api.c24
-rw-r--r--source/blender/makesrna/intern/rna_armature.c12
-rw-r--r--source/blender/makesrna/intern/rna_armature_api.c20
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c16
-rw-r--r--source/blender/makesrna/intern/rna_brush.c9
-rw-r--r--source/blender/makesrna/intern/rna_cloth.c1
-rw-r--r--source/blender/makesrna/intern/rna_color.c2
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c9
-rw-r--r--source/blender/makesrna/intern/rna_curve.c23
-rw-r--r--source/blender/makesrna/intern/rna_curve_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_curveprofile.c2
-rw-r--r--source/blender/makesrna/intern/rna_define.c26
-rw-r--r--source/blender/makesrna/intern/rna_depsgraph.c11
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c54
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c17
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c616
-rw-r--r--source/blender/makesrna/intern/rna_internal.h2
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_key.c12
-rw-r--r--source/blender/makesrna/intern/rna_lattice_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_light.c9
-rw-r--r--source/blender/makesrna/intern/rna_main.c4
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c38
-rw-r--r--source/blender/makesrna/intern/rna_material.c2
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c24
-rw-r--r--source/blender/makesrna/intern/rna_mesh_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_meta_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c17
-rw-r--r--source/blender/makesrna/intern/rna_movieclip.c4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c637
-rw-r--r--source/blender/makesrna/intern/rna_object.c217
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c4
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c7
-rw-r--r--source/blender/makesrna/intern/rna_particle.c12
-rw-r--r--source/blender/makesrna/intern/rna_pose.c21
-rw-r--r--source/blender/makesrna/intern/rna_pose_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_render.c6
-rw-r--r--source/blender/makesrna/intern/rna_rna.c25
-rw-r--r--source/blender/makesrna/intern/rna_scene.c35
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_screen.c46
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c3
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c62
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c48
-rw-r--r--source/blender/makesrna/intern/rna_shader_fx.c38
-rw-r--r--source/blender/makesrna/intern/rna_space.c282
-rw-r--r--source/blender/makesrna/intern/rna_tracking.c4
-rw-r--r--source/blender/makesrna/intern/rna_ui.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c30
-rw-r--r--source/blender/makesrna/intern/rna_volume.c16
-rw-r--r--source/blender/makesrna/intern/rna_wm.c118
-rw-r--r--source/blender/makesrna/intern/rna_wm_gizmo.c16
-rw-r--r--source/blender/modifiers/CMakeLists.txt30
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c1
-rw-r--r--source/blender/modifiers/intern/MOD_array.c6
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c1
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.c967
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.cc651
-rw-r--r--source/blender/modifiers/intern/MOD_build.c1
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c1
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c1
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c1
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c1
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c1
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c1
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c1
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c1
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c1
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c1
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_lattice.c1
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc38
-rw-r--r--source/blender/modifiers/intern/MOD_mesh_to_volume.cc19
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c1
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c1
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c8
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c1
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc888
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc1553
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.hh52
-rw-r--r--source/blender/modifiers/intern/MOD_none.c1
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c1
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c1
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c1
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c1
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c1
-rw-r--r--source/blender/modifiers/intern/MOD_shapekey.c1
-rw-r--r--source/blender/modifiers/intern/MOD_shrinkwrap.c1
-rw-r--r--source/blender/modifiers/intern/MOD_simpledeform.c198
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c64
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c2
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c1
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c17
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c1
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c29
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c1
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c1
-rw-r--r--source/blender/modifiers/intern/MOD_volume_displace.cc22
-rw-r--r--source/blender/modifiers/intern/MOD_volume_to_mesh.cc5
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c1
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weld.c1
-rw-r--r--source/blender/modifiers/intern/MOD_wireframe.c1
-rw-r--r--source/blender/nodes/CMakeLists.txt59
-rw-r--r--source/blender/nodes/NOD_composite.h1
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh47
-rw-r--r--source/blender/nodes/NOD_geometry.h22
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh236
-rw-r--r--source/blender/nodes/NOD_math_functions.hh5
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh46
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh70
-rw-r--r--source/blender/nodes/NOD_socket.h2
-rw-r--r--source/blender/nodes/NOD_static_types.h84
-rw-r--r--source/blender/nodes/NOD_type_conversions.hh83
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c7
-rw-r--r--source/blender/nodes/composite/node_composite_util.c10
-rw-r--r--source/blender/nodes/composite/node_composite_util.h4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.c65
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc133
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.c34
-rw-r--r--source/blender/nodes/function/node_function_util.cc10
-rw-r--r--source/blender/nodes/function/node_function_util.hh1
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc32
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc10
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc192
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc284
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc55
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc31
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc118
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc93
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc232
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc39
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc437
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc106
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc166
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc96
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc68
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc597
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc268
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc352
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc170
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc177
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc43
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_common.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc321
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc58
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc211
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc313
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc678
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material.cc51
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc123
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc107
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_replace.cc75
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc372
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc)66
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc252
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc316
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc190
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc222
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc101
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_scale.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_translate.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc106
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivide.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc191
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc60
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc16
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc50
-rw-r--r--source/blender/nodes/intern/math_functions.cc2
-rw-r--r--source/blender/nodes/intern/node_common.c20
-rw-r--r--source/blender/nodes/intern/node_common.h4
-rw-r--r--source/blender/nodes/intern/node_exec.cc (renamed from source/blender/nodes/intern/node_exec.c)49
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc101
-rw-r--r--source/blender/nodes/intern/node_socket.cc137
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc128
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc223
-rw-r--r--source/blender/nodes/intern/node_util.c125
-rw-r--r--source/blender/nodes/intern/type_callbacks.cc3
-rw-r--r--source/blender/nodes/intern/type_conversions.cc349
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c38
-rw-r--r--source/blender/nodes/shader/node_shader_util.c32
-rw-r--r--source/blender/nodes/shader/node_shader_util.h10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc (renamed from source/blender/nodes/shader/nodes/node_shader_curves.c)112
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc182
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.cc (renamed from source/blender/nodes/shader/nodes/node_shader_mixRgb.c)69
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_aov.c5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc13
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.c19
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc199
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_wavelength.c28
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c7
-rw-r--r--source/blender/nodes/texture/node_texture_util.c10
-rw-r--r--source/blender/nodes/texture/node_texture_util.h4
-rw-r--r--source/blender/python/BPY_extern_run.h24
-rw-r--r--source/blender/python/bmesh/bmesh_py_api.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c65
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.h27
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.h9
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_meshdata.c9
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_meshdata.h3
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_select.h6
-rw-r--r--source/blender/python/bmesh/bmesh_py_utils.c6
-rw-r--r--source/blender/python/generic/bgl.h3
-rw-r--r--source/blender/python/generic/idprop_py_api.c567
-rw-r--r--source/blender/python/generic/idprop_py_api.h61
-rw-r--r--source/blender/python/generic/imbuf_py_api.c18
-rw-r--r--source/blender/python/generic/py_capi_utils.c1
-rw-r--r--source/blender/python/gpu/CMakeLists.txt4
-rw-r--r--source/blender/python/gpu/gpu_py_api.c8
-rw-r--r--source/blender/python/gpu/gpu_py_batch.h4
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.c176
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.h3
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.c148
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.h23
-rw-r--r--source/blender/python/gpu/gpu_py_element.h3
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.c164
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.h7
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c20
-rw-r--r--source/blender/python/gpu/gpu_py_platform.c81
-rw-r--r--source/blender/python/gpu/gpu_py_platform.h (renamed from source/blender/compositor/intern/COM_SocketReader.cc)10
-rw-r--r--source/blender/python/gpu/gpu_py_shader.h3
-rw-r--r--source/blender/python/gpu/gpu_py_state.c41
-rw-r--r--source/blender/python/gpu/gpu_py_texture.c43
-rw-r--r--source/blender/python/gpu/gpu_py_texture.h3
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_buffer.h4
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_format.h3
-rw-r--r--source/blender/python/intern/CMakeLists.txt2
-rw-r--r--source/blender/python/intern/bpy.c10
-rw-r--r--source/blender/python/intern/bpy_app.c5
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c4
-rw-r--r--source/blender/python/intern/bpy_capi_utils.h5
-rw-r--r--source/blender/python/intern/bpy_interface.c13
-rw-r--r--source/blender/python/intern/bpy_interface_run.c67
-rw-r--r--source/blender/python/intern/bpy_library_load.c2
-rw-r--r--source/blender/python/intern/bpy_msgbus.c2
-rw-r--r--source/blender/python/intern/bpy_operator.c8
-rw-r--r--source/blender/python/intern/bpy_props.c32
-rw-r--r--source/blender/python/intern/bpy_rna.c45
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c4
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c2
-rw-r--r--source/blender/python/intern/bpy_rna_operator.c152
-rw-r--r--source/blender/python/intern/bpy_rna_operator.h31
-rw-r--r--source/blender/python/intern/bpy_rna_types_capi.c18
-rw-r--r--source/blender/python/intern/bpy_utils_units.c4
-rw-r--r--source/blender/python/mathutils/mathutils.c12
-rw-r--r--source/blender/python/mathutils/mathutils.h3
-rw-r--r--source/blender/python/mathutils/mathutils_Euler.c2
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c148
-rw-r--r--source/blender/python/mathutils/mathutils_bvhtree.c7
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c2
-rw-r--r--source/blender/python/mathutils/mathutils_interpolate.c15
-rw-r--r--source/blender/python/mathutils/mathutils_kdtree.c4
-rw-r--r--source/blender/python/rna_dump.py2
-rw-r--r--source/blender/python/simple_enum_gen.py63
-rw-r--r--source/blender/render/RE_engine.h4
-rw-r--r--source/blender/render/RE_pipeline.h19
-rw-r--r--source/blender/render/intern/engine.c137
-rw-r--r--source/blender/render/intern/pipeline.c273
-rw-r--r--source/blender/render/intern/render_result.c2
-rw-r--r--source/blender/render/intern/render_types.h2
-rw-r--r--source/blender/render/intern/zbuf.c2
-rw-r--r--source/blender/sequencer/CMakeLists.txt2
-rw-r--r--source/blender/sequencer/SEQ_add.h1
-rw-r--r--source/blender/sequencer/SEQ_iterator.h80
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h5
-rw-r--r--source/blender/sequencer/SEQ_time.h1
-rw-r--r--source/blender/sequencer/SEQ_transform.h4
-rw-r--r--source/blender/sequencer/SEQ_utils.h9
-rw-r--r--source/blender/sequencer/intern/effects.c5
-rw-r--r--source/blender/sequencer/intern/image_cache.c39
-rw-r--r--source/blender/sequencer/intern/iterator.c300
-rw-r--r--source/blender/sequencer/intern/proxy.c24
-rw-r--r--source/blender/sequencer/intern/render.c416
-rw-r--r--source/blender/sequencer/intern/render.h4
-rw-r--r--source/blender/sequencer/intern/sequencer.c48
-rw-r--r--source/blender/sequencer/intern/strip_add.c43
-rw-r--r--source/blender/sequencer/intern/strip_edit.c114
-rw-r--r--source/blender/sequencer/intern/strip_relations.c5
-rw-r--r--source/blender/sequencer/intern/strip_time.c79
-rw-r--r--source/blender/sequencer/intern/strip_transform.c40
-rw-r--r--source/blender/sequencer/intern/utils.c94
-rw-r--r--source/blender/shader_fx/intern/FX_shader_glow.c7
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/WM_api.h85
-rw-r--r--source/blender/windowmanager/WM_types.h30
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_api.h6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c17
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c8
-rw-r--r--source/blender/windowmanager/gizmo/wm_gizmo_fn.h4
-rw-r--r--source/blender/windowmanager/intern/wm.c1
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c2
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c17
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c8
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c17
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c86
-rw-r--r--source/blender/windowmanager/intern/wm_files.c81
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c50
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c4
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c1
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c11
-rw-r--r--source/blender/windowmanager/intern/wm_keymap.c9
-rw-r--r--source/blender/windowmanager/intern/wm_operator_type.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c11
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c484
-rw-r--r--source/blender/windowmanager/intern/wm_window.c34
-rw-r--r--source/blender/windowmanager/wm.h1
-rw-r--r--source/blender/windowmanager/wm_files.h3
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr.c5
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actions.c480
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c6
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h68
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c148
-rw-r--r--source/creator/CMakeLists.txt95
-rw-r--r--source/creator/blender_launcher_win32.c92
-rw-r--r--source/creator/creator.c40
-rw-r--r--source/creator/creator_args.c34
-rw-r--r--source/creator/creator_intern.h2
m---------source/tools0
1758 files changed, 71554 insertions, 32975 deletions
diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt
index cb121cb9c8d..b42ca284ecb 100644
--- a/source/blender/blendthumb/CMakeLists.txt
+++ b/source/blender/blendthumb/CMakeLists.txt
@@ -31,6 +31,7 @@ set(SRC
string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG " /nodefaultlib:MSVCRT.lib")
add_library(BlendThumb SHARED ${SRC})
+setup_platform_linker_flags(BlendThumb)
target_link_libraries(BlendThumb ${ZLIB_LIBRARIES})
install(
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index f83ee409187..b7c226ada1d 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -41,6 +41,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
@@ -640,18 +641,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
(size_t)buf_info->ch);
float *fbuf = buf_info->fbuf + buf_ofs;
- if (a >= 1.0f) {
- fbuf[0] = b_col_float[0];
- fbuf[1] = b_col_float[1];
- fbuf[2] = b_col_float[2];
- fbuf[3] = 1.0f;
- }
- else {
- fbuf[0] = (b_col_float[0] * a) + (fbuf[0] * (1.0f - a));
- fbuf[1] = (b_col_float[1] * a) + (fbuf[1] * (1.0f - a));
- fbuf[2] = (b_col_float[2] * a) + (fbuf[2] * (1.0f - a));
- fbuf[3] = MIN2(fbuf[3] + a, 1.0f); /* clamp to 1.0 */
- }
+ float font_pixel[4];
+ font_pixel[0] = b_col_float[0] * a;
+ font_pixel[1] = b_col_float[1] * a;
+ font_pixel[2] = b_col_float[2] * a;
+ font_pixel[3] = a;
+ blend_color_mix_float(fbuf, fbuf, font_pixel);
}
}
@@ -677,19 +672,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
(size_t)buf_info->ch);
unsigned char *cbuf = buf_info->cbuf + buf_ofs;
- if (a >= 1.0f) {
- cbuf[0] = b_col_char[0];
- cbuf[1] = b_col_char[1];
- cbuf[2] = b_col_char[2];
- cbuf[3] = 255;
- }
- else {
- cbuf[0] = (unsigned char)((b_col_char[0] * a) + (cbuf[0] * (1.0f - a)));
- cbuf[1] = (unsigned char)((b_col_char[1] * a) + (cbuf[1] * (1.0f - a)));
- cbuf[2] = (unsigned char)((b_col_char[2] * a) + (cbuf[2] * (1.0f - a)));
- /* clamp to 255 */
- cbuf[3] = (unsigned char)MIN2((int)cbuf[3] + (int)(a * 255), 255);
- }
+ uchar font_pixel[4];
+ font_pixel[0] = b_col_char[0];
+ font_pixel[1] = b_col_char[1];
+ font_pixel[2] = b_col_char[2];
+ font_pixel[3] = unit_float_to_uchar_clamp(a);
+ blend_color_mix_byte(cbuf, cbuf, font_pixel);
}
}
diff --git a/source/blender/blenfont/intern/blf_font_win32_compat.c b/source/blender/blenfont/intern/blf_font_win32_compat.c
index 7d130204c07..e573d3bd224 100644
--- a/source/blender/blenfont/intern/blf_font_win32_compat.c
+++ b/source/blender/blenfont/intern/blf_font_win32_compat.c
@@ -66,7 +66,7 @@ static unsigned long ft_ansi_stream_io(FT_Stream stream,
file = STREAM_FILE(stream);
if (stream->pos != offset) {
- fseek(file, offset, SEEK_SET);
+ BLI_fseek(file, offset, SEEK_SET);
}
return fread(buffer, 1, count, file);
@@ -93,7 +93,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep
return FT_THROW(Cannot_Open_Resource);
}
- fseek(file, 0, SEEK_END);
+ BLI_fseek(file, 0LL, SEEK_END);
stream->size = ftell(file);
if (!stream->size) {
fprintf(stderr,
@@ -104,7 +104,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep
return FT_THROW(Cannot_Open_Stream);
}
- fseek(file, 0, SEEK_SET);
+ BLI_fseek(file, 0LL, SEEK_SET);
stream->descriptor.pointer = file;
stream->read = ft_ansi_stream_io;
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index f3c5c057dec..2ec0ca22865 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -506,7 +506,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
if (gc->texture) {
GPU_texture_free(gc->texture);
}
- gc->texture = GPU_texture_create_1d_array(__func__, w, h, 1, GPU_R8, NULL);
+ gc->texture = GPU_texture_create_2d(__func__, w, h, 1, GPU_R8, NULL);
gc->bitmap_len_landed = 0;
}
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index 717cfa607ad..3d81fcba37d 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -140,7 +140,7 @@ void BKE_pose_channel_free_bbone_cache(struct bPoseChannel_Runtime *runtime);
void BKE_pose_channels_free(struct bPose *pose);
void BKE_pose_channels_free_ex(struct bPose *pose, bool do_id_user);
-void BKE_pose_channels_hash_make(struct bPose *pose);
+void BKE_pose_channels_hash_ensure(struct bPose *pose);
void BKE_pose_channels_hash_free(struct bPose *pose);
void BKE_pose_channels_remove(struct Object *ob,
@@ -161,7 +161,7 @@ void BKE_pose_channel_session_uuid_generate(struct bPoseChannel *pchan);
struct bPoseChannel *BKE_pose_channel_find_name(const struct bPose *pose, const char *name);
struct bPoseChannel *BKE_pose_channel_active(struct Object *ob);
struct bPoseChannel *BKE_pose_channel_active_or_first_selected(struct Object *ob);
-struct bPoseChannel *BKE_pose_channel_verify(struct bPose *pose, const char *name);
+struct bPoseChannel *BKE_pose_channel_ensure(struct bPose *pose, const char *name);
struct bPoseChannel *BKE_pose_channel_get_mirrored(const struct bPose *pose, const char *name);
void BKE_pose_check_uuids_unique_and_report(const struct bPose *pose);
@@ -188,10 +188,6 @@ void BKE_pose_itasc_init(struct bItasc *itasc);
/* Checks if a bone is part of an IK chain or not */
bool BKE_pose_channel_in_IK_chain(struct Object *ob, struct bPoseChannel *pchan);
-/* clears BONE_UNKEYED flags for frame changing */
-// XXX to be deprecated for a more general solution in animsys...
-void framechange_poses_clear_unkeyed(struct Main *bmain);
-
/* Bone Groups API --------------------- */
/* Adds a new bone-group */
@@ -227,6 +223,9 @@ void BKE_pose_blend_read_data(struct BlendDataReader *reader, struct bPose *pose
void BKE_pose_blend_read_lib(struct BlendLibReader *reader, struct Object *ob, struct bPose *pose);
void BKE_pose_blend_read_expand(struct BlendExpander *expander, struct bPose *pose);
+/* action_mirror.c */
+void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm);
+
#ifdef __cplusplus
};
#endif
diff --git a/source/blender/blenkernel/BKE_anim_path.h b/source/blender/blenkernel/BKE_anim_path.h
index ae2d13530ed..9db63080fd9 100644
--- a/source/blender/blenkernel/BKE_anim_path.h
+++ b/source/blender/blenkernel/BKE_anim_path.h
@@ -26,22 +26,28 @@
extern "C" {
#endif
-struct ListBase;
+struct CurveCache;
struct Object;
-struct Path;
/* ---------------------------------------------------- */
/* Curve Paths */
-void free_path(struct Path *path);
-void calc_curvepath(struct Object *ob, struct ListBase *nurbs);
-bool where_on_path(const struct Object *ob,
- float ctime,
- float r_vec[4],
- float r_dir[3],
- float r_quat[4],
- float *r_radius,
- float *r_weight);
+int BKE_anim_path_get_array_size(const struct CurveCache *curve_cache);
+float BKE_anim_path_get_length(const struct CurveCache *curve_cache);
+
+/* This function populates the 'ob->runtime.curve_cache->anim_path_accum_length' data.
+ * You should never have to call this manually as it should already have been called by
+ * 'BKE_displist_make_curveTypes'. Do not call this manually unless you know what you are doing.
+ */
+void BKE_anim_path_calc_data(struct Object *ob);
+
+bool BKE_where_on_path(const struct Object *ob,
+ float ctime,
+ float r_vec[4],
+ float r_dir[3],
+ float r_quat[4],
+ float *r_radius,
+ float *r_weight);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index a0ba1415b41..3002a9cc10d 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -178,6 +178,7 @@ void BKE_armature_where_is_bone(struct Bone *bone,
void BKE_pose_clear_pointers(struct bPose *pose);
void BKE_pose_remap_bone_pointers(struct bArmature *armature, struct bPose *pose);
void BKE_pchan_rebuild_bbone_handles(struct bPose *pose, struct bPoseChannel *pchan);
+void BKE_pose_channels_clear_with_null_bone(struct bPose *pose, const bool do_id_user);
void BKE_pose_rebuild(struct Main *bmain,
struct Object *ob,
struct bArmature *arm,
@@ -342,6 +343,8 @@ void BKE_pchan_bbone_deform_segment_index(const struct bPoseChannel *pchan,
#define PBONE_SELECTABLE(arm, bone) \
(PBONE_VISIBLE(arm, bone) && !((bone)->flag & BONE_UNSELECTABLE))
+#define PBONE_SELECTED(arm, bone) (((bone)->flag & BONE_SELECTED) & PBONE_VISIBLE(arm, bone))
+
/* context.selected_pose_bones */
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN(_ob, _pchan) \
for (bPoseChannel *_pchan = (_ob)->pose->chanbase.first; _pchan; _pchan = _pchan->next) { \
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index 120b4e08b9c..c3f7dbd4bd9 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -20,299 +20,342 @@
#include "FN_cpp_type.hh"
#include "FN_generic_span.hh"
+#include "FN_generic_virtual_array.hh"
#include "BKE_attribute.h"
#include "BLI_color.hh"
#include "BLI_float2.hh"
#include "BLI_float3.hh"
-
-namespace blender::bke {
-
-using fn::CPPType;
-
-const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
-CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
-CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
-AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
+#include "BLI_function_ref.hh"
/**
- * This class offers an indirection for reading an attribute.
- * This is useful for the following reasons:
- * - Blender does not store all attributes the same way.
- * The simplest case are custom data layers with primitive types.
- * A bit more complex are mesh attributes like the position of vertices,
- * which are embedded into the MVert struct.
- * Even more complex to access are vertex weights.
- * - Sometimes attributes are stored on one domain, but we want to access
- * the attribute on a different domain. Therefore, we have to interpolate
- * between the domains.
+ * Contains information about an attribute in a geometry component.
+ * More information can be added in the future. E.g. whether the attribute is builtin and how it is
+ * stored (uv map, vertex group, ...).
*/
-class ReadAttribute {
- protected:
- const AttributeDomain domain_;
- const CPPType &cpp_type_;
- const CustomDataType custom_data_type_;
- const int64_t size_;
-
- /* Protects the span below, so that no two threads initialize it at the same time. */
- mutable std::mutex span_mutex_;
- /* When it is not null, it points to the attribute array or a temporary array that contains all
- * the attribute values. */
- mutable void *array_buffer_ = nullptr;
- /* Is true when the buffer above is owned by the attribute accessor. */
- mutable bool array_is_temporary_ = false;
+struct AttributeMetaData {
+ AttributeDomain domain;
+ CustomDataType data_type;
- public:
- ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
- : domain_(domain),
- cpp_type_(cpp_type),
- custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
- size_(size)
+ constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
{
+ return (a.domain == b.domain) && (a.data_type == b.data_type);
}
+};
- virtual ~ReadAttribute();
-
- AttributeDomain domain() const
+/**
+ * Base class for the attribute initializer types described below.
+ */
+struct AttributeInit {
+ enum class Type {
+ Default,
+ VArray,
+ MoveArray,
+ };
+ Type type;
+ AttributeInit(const Type type) : type(type)
{
- return domain_;
}
+};
- const CPPType &cpp_type() const
+/**
+ * Create an attribute using the default value for the data type.
+ * The default values may depend on the attribute provider implementation.
+ */
+struct AttributeInitDefault : public AttributeInit {
+ AttributeInitDefault() : AttributeInit(Type::Default)
{
- return cpp_type_;
}
+};
- CustomDataType custom_data_type() const
- {
- return custom_data_type_;
- }
+/**
+ * Create an attribute by copying data from an existing virtual array. The virtual array
+ * must have the same type as the newly created attribute.
+ *
+ * Note that this can be used to fill the new attribute with the default
+ */
+struct AttributeInitVArray : public AttributeInit {
+ const blender::fn::GVArray *varray;
- int64_t size() const
+ AttributeInitVArray(const blender::fn::GVArray *varray)
+ : AttributeInit(Type::VArray), varray(varray)
{
- return size_;
}
+};
+
+/**
+ * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
+ * Sometimes data is created before a geometry component is available. In that case, it's
+ * preferable to move data directly to the created attribute to avoid a new allocation and a copy.
+ *
+ * Note that this will only have a benefit for attributes that are stored directly as contiguous
+ * arrays, so not for some built-in attributes.
+ *
+ * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
+ * can't be used directly, and that is generally how Blender expects custom data to be allocated.
+ */
+struct AttributeInitMove : public AttributeInit {
+ void *data = nullptr;
- void get(const int64_t index, void *r_value) const
+ AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
{
- BLI_assert(index < size_);
- this->get_internal(index, r_value);
}
+};
+
+/* Returns false when the iteration should be stopped. */
+using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
+ const AttributeMetaData &meta_data)>;
- /* Get a span that contains all attribute values. */
- fn::GSpan get_span() const;
+namespace blender::bke {
- template<typename T> Span<T> get_span() const
+using fn::CPPType;
+using fn::GVArray;
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArrayPtr;
+
+const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
+CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
+CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
+AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
+
+/**
+ * Used when looking up a "plain attribute" based on a name for reading from it.
+ */
+struct ReadAttributeLookup {
+ /* The virtual array that is used to read from this attribute. */
+ GVArrayPtr varray;
+ /* Domain the attribute lives on in the geometry. */
+ AttributeDomain domain;
+
+ /* Convenience function to check if the attribute has been found. */
+ operator bool() const
{
- return this->get_span().typed<T>();
+ return this->varray.get() != nullptr;
}
+};
- protected:
- /* r_value is expected to be uninitialized. */
- virtual void get_internal(const int64_t index, void *r_value) const = 0;
-
- virtual void initialize_span() const;
+/**
+ * Used when looking up a "plain attribute" based on a name for reading from it and writing to it.
+ */
+struct WriteAttributeLookup {
+ /* The virtual array that is used to read from and write to the attribute. */
+ GVMutableArrayPtr varray;
+ /* Domain the attributes lives on in the geometry. */
+ AttributeDomain domain;
+
+ /* Convenience function to check if the attribute has been found. */
+ operator bool() const
+ {
+ return this->varray.get() != nullptr;
+ }
};
/**
- * This exists for similar reasons as the ReadAttribute class, except that
- * it does not deal with interpolation between domains.
+ * An output attribute allows writing to an attribute (and optionally reading as well). It adds
+ * some convenience features on top of `GVMutableArray` that are very commonly used.
+ *
+ * Supported convenience features:
+ * - Implicit type conversion when writing to builtin attributes.
+ * - Supports simple access to a span containing the attribute values (that avoids the use of
+ * VMutableArray_Span in many cases).
+ * - An output attribute can live side by side with an existing attribute with a different domain
+ * or data type. The old attribute will only be overwritten when the #save function is called.
*/
-class WriteAttribute {
- protected:
- const AttributeDomain domain_;
- const CPPType &cpp_type_;
- const CustomDataType custom_data_type_;
- const int64_t size_;
-
- /* When not null, this points either to the attribute array or to a temporary array. */
- void *array_buffer_ = nullptr;
- /* True, when the buffer points to a temporary array. */
- bool array_is_temporary_ = false;
- /* This helps to protect against forgetting to apply changes done to the array. */
- bool array_should_be_applied_ = false;
+class OutputAttribute {
+ public:
+ using SaveFn = std::function<void(OutputAttribute &)>;
+
+ private:
+ GVMutableArrayPtr varray_;
+ AttributeDomain domain_;
+ SaveFn save_;
+ std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
+ bool ignore_old_values_ = false;
+ bool save_has_been_called_ = false;
public:
- WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
- : domain_(domain),
- cpp_type_(cpp_type),
- custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
- size_(size)
+ OutputAttribute() = default;
+
+ OutputAttribute(GVMutableArrayPtr varray,
+ AttributeDomain domain,
+ SaveFn save,
+ const bool ignore_old_values)
+ : varray_(std::move(varray)),
+ domain_(domain),
+ save_(std::move(save)),
+ ignore_old_values_(ignore_old_values)
{
}
- virtual ~WriteAttribute();
+ OutputAttribute(OutputAttribute &&other) = default;
- AttributeDomain domain() const
+ ~OutputAttribute();
+
+ operator bool() const
{
- return domain_;
+ return varray_.get() != nullptr;
}
- const CPPType &cpp_type() const
+ GVMutableArray &operator*()
{
- return cpp_type_;
+ return *varray_;
}
- CustomDataType custom_data_type() const
+ GVMutableArray *operator->()
{
- return custom_data_type_;
+ return varray_.get();
}
- int64_t size() const
+ GVMutableArray &varray()
{
- return size_;
+ return *varray_;
}
- void get(const int64_t index, void *r_value) const
+ AttributeDomain domain() const
{
- BLI_assert(index < size_);
- this->get_internal(index, r_value);
+ return domain_;
}
- void set(const int64_t index, const void *value)
+ const CPPType &cpp_type() const
{
- BLI_assert(index < size_);
- this->set_internal(index, value);
+ return varray_->type();
}
- /* Get a span that new attribute values can be written into. When all values have been changed,
- * #apply_span has to be called. */
- fn::GMutableSpan get_span();
- /* The span returned by this method might not contain the current attribute values. */
- fn::GMutableSpan get_span_for_write_only();
- /* Write the changes to the span into the actual attribute, if they aren't already. */
- void apply_span();
-
- template<typename T> MutableSpan<T> get_span()
+ CustomDataType custom_data_type() const
{
- return this->get_span().typed<T>();
+ return cpp_type_to_custom_data_type(this->cpp_type());
}
- template<typename T> MutableSpan<T> get_span_for_write_only()
+ fn::GMutableSpan as_span()
{
- return this->get_span_for_write_only().typed<T>();
+ if (!optional_span_varray_.has_value()) {
+ const bool materialize_old_values = !ignore_old_values_;
+ optional_span_varray_.emplace(*varray_, materialize_old_values);
+ }
+ fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
+ return span_varray;
}
- protected:
- virtual void get_internal(const int64_t index, void *r_value) const = 0;
- virtual void set_internal(const int64_t index, const void *value) = 0;
+ template<typename T> MutableSpan<T> as_span()
+ {
+ return this->as_span().typed<T>();
+ }
- virtual void initialize_span(const bool write_only);
- virtual void apply_span_if_necessary();
+ void save();
};
-using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
-using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
-
-/* This provides type safe access to an attribute.
- * The underlying ReadAttribute is owned optionally. */
-template<typename T> class TypedReadAttribute {
+/**
+ * Same as OutputAttribute, but should be used when the data type is known at compile time.
+ */
+template<typename T> class OutputAttribute_Typed {
private:
- std::unique_ptr<const ReadAttribute> owned_attribute_;
- const ReadAttribute *attribute_;
+ OutputAttribute attribute_;
+ std::optional<fn::GVMutableArray_Typed<T>> optional_varray_;
+ VMutableArray<T> *varray_ = nullptr;
public:
- TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute)
+ OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
{
- owned_attribute_ = std::move(attribute);
- BLI_assert(owned_attribute_);
+ if (attribute_) {
+ optional_varray_.emplace(attribute_.varray());
+ varray_ = &**optional_varray_;
+ }
}
- TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute)
+ operator bool() const
{
- BLI_assert(attribute_->cpp_type().is<T>());
+ return varray_ != nullptr;
}
- int64_t size() const
+ VMutableArray<T> &operator*()
{
- return attribute_->size();
+ return *varray_;
}
- T operator[](const int64_t index) const
+ VMutableArray<T> *operator->()
{
- BLI_assert(index < attribute_->size());
- T value;
- value.~T();
- attribute_->get(index, &value);
- return value;
+ return varray_;
}
- /* Get a span to that contains all attribute values for faster and more convenient access. */
- Span<T> get_span() const
+ VMutableArray<T> &varray()
{
- return attribute_->get_span().template typed<T>();
+ return *varray_;
}
-};
-
-/* This provides type safe access to an attribute.
- * The underlying WriteAttribute is owned optionally. */
-template<typename T> class TypedWriteAttribute {
- private:
- std::unique_ptr<WriteAttribute> owned_attribute_;
- WriteAttribute *attribute_;
- public:
- TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute)
+ AttributeDomain domain() const
{
- owned_attribute_ = std::move(attribute);
- BLI_assert(owned_attribute_);
+ return attribute_.domain();
}
- TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute)
+ const CPPType &cpp_type() const
{
- BLI_assert(attribute_->cpp_type().is<T>());
+ return CPPType::get<T>();
}
- int64_t size() const
+ CustomDataType custom_data_type() const
{
- return attribute_->size();
+ return cpp_type_to_custom_data_type(this->cpp_type());
}
- T operator[](const int64_t index) const
+ MutableSpan<T> as_span()
{
- BLI_assert(index < attribute_->size());
- T value;
- value.~T();
- attribute_->get(index, &value);
- return value;
+ return attribute_.as_span<T>();
}
- void set(const int64_t index, const T &value)
+ void save()
{
- attribute_->set(index, &value);
+ attribute_.save();
}
+};
- /* Get a span that new values can be written into. Once all values have been updated #apply_span
- * has to be called. */
- MutableSpan<T> get_span()
- {
- return attribute_->get_span().typed<T>();
- }
- /* The span returned by this method might not contain the current attribute values. */
- MutableSpan<T> get_span_for_write_only()
- {
- return attribute_->get_span_for_write_only().typed<T>();
- }
+/**
+ * A basic container around DNA CustomData so that its users
+ * don't have to implement special copy and move constructors.
+ */
+class CustomDataAttributes {
+ /**
+ * #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct
+ * itself, so keep track of the size here so this class can implement its own destructor.
+ * If the implementation of the attribute storage changes, this could be removed.
+ */
+ int size_;
+
+ public:
+ CustomData data;
+
+ CustomDataAttributes();
+ ~CustomDataAttributes();
+ CustomDataAttributes(const CustomDataAttributes &other);
+ CustomDataAttributes(CustomDataAttributes &&other);
+ CustomDataAttributes &operator=(const CustomDataAttributes &other);
+
+ void reallocate(const int size);
- /* Write back all changes to the actual attribute, if necessary. */
- void apply_span()
+ std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const;
+
+ blender::fn::GVArrayPtr get_for_read(const StringRef name,
+ const CustomDataType data_type,
+ const void *default_value) const;
+
+ template<typename T>
+ blender::fn::GVArray_Typed<T> get_for_read(const blender::StringRef name,
+ const T &default_value) const
{
- attribute_->apply_span();
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ GVArrayPtr varray = this->get_for_read(name, type, &default_value);
+ return blender::fn::GVArray_Typed<T>(std::move(varray));
}
-};
-using BooleanReadAttribute = TypedReadAttribute<bool>;
-using FloatReadAttribute = TypedReadAttribute<float>;
-using Float2ReadAttribute = TypedReadAttribute<float2>;
-using Float3ReadAttribute = TypedReadAttribute<float3>;
-using Int32ReadAttribute = TypedReadAttribute<int>;
-using Color4fReadAttribute = TypedReadAttribute<Color4f>;
-using BooleanWriteAttribute = TypedWriteAttribute<bool>;
-using FloatWriteAttribute = TypedWriteAttribute<float>;
-using Float2WriteAttribute = TypedWriteAttribute<float2>;
-using Float3WriteAttribute = TypedWriteAttribute<float3>;
-using Int32WriteAttribute = TypedWriteAttribute<int>;
-using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
+ std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name);
+ bool create(const blender::StringRef name, const CustomDataType data_type);
+ bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
+ bool remove(const blender::StringRef name);
+
+ bool foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const;
+};
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh
index 16fc0db60fb..ba683362e69 100644
--- a/source/blender/blenkernel/BKE_attribute_math.hh
+++ b/source/blender/blenkernel/BKE_attribute_math.hh
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
+
#include "BLI_array.hh"
#include "BLI_color.hh"
#include "BLI_float2.hh"
@@ -21,13 +23,17 @@
#include "DNA_customdata_types.h"
+#include "FN_cpp_type.hh"
+
namespace blender::attribute_math {
+using fn::CPPType;
+
/**
* Utility function that simplifies calling a templated function based on a custom data type.
*/
template<typename Func>
-void convert_to_static_type(const CustomDataType data_type, const Func &func)
+inline void convert_to_static_type(const CustomDataType data_type, const Func &func)
{
switch (data_type) {
case CD_PROP_FLOAT:
@@ -46,7 +52,7 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
func(bool());
break;
case CD_PROP_COLOR:
- func(Color4f());
+ func(ColorGeometry4f());
break;
default:
BLI_assert_unreachable();
@@ -54,6 +60,32 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
}
}
+template<typename Func>
+inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func)
+{
+ if (cpp_type.is<float>()) {
+ func(float());
+ }
+ else if (cpp_type.is<float2>()) {
+ func(float2());
+ }
+ else if (cpp_type.is<float3>()) {
+ func(float3());
+ }
+ else if (cpp_type.is<int>()) {
+ func(int());
+ }
+ else if (cpp_type.is<bool>()) {
+ func(bool());
+ }
+ else if (cpp_type.is<ColorGeometry4f>()) {
+ func(ColorGeometry4f());
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+}
+
/* -------------------------------------------------------------------- */
/** \name Mix three values of the same type.
*
@@ -91,9 +123,12 @@ inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, co
}
template<>
-inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, const Color4f &v2)
+inline ColorGeometry4f mix3(const float3 &weights,
+ const ColorGeometry4f &v0,
+ const ColorGeometry4f &v1,
+ const ColorGeometry4f &v2)
{
- Color4f result;
+ ColorGeometry4f result;
interp_v4_v4v4v4(result, v0, v1, v2, weights);
return result;
}
@@ -101,6 +136,49 @@ inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Mix two values of the same type.
+ *
+ * This is just basic linear interpolation.
+ * \{ */
+
+template<typename T> T mix2(const float factor, const T &a, const T &b);
+
+template<> inline bool mix2(const float factor, const bool &a, const bool &b)
+{
+ return ((1.0f - factor) * a + factor * b) >= 0.5f;
+}
+
+template<> inline int mix2(const float factor, const int &a, const int &b)
+{
+ return static_cast<int>((1.0f - factor) * a + factor * b);
+}
+
+template<> inline float mix2(const float factor, const float &a, const float &b)
+{
+ return (1.0f - factor) * a + factor * b;
+}
+
+template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b)
+{
+ return float2::interpolate(a, b, factor);
+}
+
+template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
+{
+ return float3::interpolate(a, b, factor);
+}
+
+template<>
+inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
+{
+ ColorGeometry4f result;
+ interp_v4_v4v4(result, a, b, factor);
+ return result;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Mix a dynamic amount of values with weights for many elements.
*
* This section provides an abstraction for "mixers". The abstraction encapsulates details about
@@ -153,8 +231,10 @@ template<typename T> class SimpleMixer {
}
};
-/** This mixer accumulates values in a type that is different from the one that is mixed. Some
- * types cannot encode the floating point weights in their values (e.g. int and bool). */
+/**
+ * This mixer accumulates values in a type that is different from the one that is mixed.
+ * Some types cannot encode the floating point weights in their values (e.g. int and bool).
+ */
template<typename T, typename AccumulationT, T (*ConvertToT)(const AccumulationT &value)>
class SimpleMixerWithAccumulationType {
private:
@@ -198,15 +278,16 @@ class SimpleMixerWithAccumulationType {
}
};
-class Color4fMixer {
+class ColorGeometryMixer {
private:
- MutableSpan<Color4f> buffer_;
- Color4f default_color_;
+ MutableSpan<ColorGeometry4f> buffer_;
+ ColorGeometry4f default_color_;
Array<float> total_weights_;
public:
- Color4fMixer(MutableSpan<Color4f> buffer, Color4f default_color = {0, 0, 0, 1});
- void mix_in(const int64_t index, const Color4f &color, const float weight = 1.0f);
+ ColorGeometryMixer(MutableSpan<ColorGeometry4f> buffer,
+ ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ void mix_in(const int64_t index, const ColorGeometry4f &color, const float weight = 1.0f);
void finalize();
};
@@ -223,10 +304,10 @@ template<> struct DefaultMixerStruct<float2> {
template<> struct DefaultMixerStruct<float3> {
using type = SimpleMixer<float3>;
};
-template<> struct DefaultMixerStruct<Color4f> {
- /* Use a special mixer for colors. Color4f can't be added/multiplied, because this is not
+template<> struct DefaultMixerStruct<ColorGeometry4f> {
+ /* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not
* something one should usually do with colors. */
- using type = Color4fMixer;
+ using type = ColorGeometryMixer;
};
template<> struct DefaultMixerStruct<int> {
static int double_to_int(const double &value)
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index c9585430ae2..6ad910ff8ab 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -31,7 +31,7 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 293
+#define BLENDER_VERSION 300
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 14
+#define BLENDER_FILE_SUBVERSION 3
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h
index fadba5644de..ef2a0ed34a0 100644
--- a/source/blender/blenkernel/BKE_callbacks.h
+++ b/source/blender/blenkernel/BKE_callbacks.h
@@ -30,11 +30,60 @@ struct Main;
struct PointerRNA;
/**
- * Common suffix uses:
- * - ``_PRE/_POST``:
- * For handling discrete non-interactive events.
- * - ``_INIT/_COMPLETE/_CANCEL``:
- * For handling jobs (which may in turn cause other handlers to be called).
+ Callbacks for One Off Actions
+ * =============================
+ *
+ * - `{ACTION}` use in cases where only a single callback is required,
+ * `VERSION_UPDATE` and `RENDER_STATS` for example.
+ *
+ * \note avoid single callbacks if there is a chance `PRE/POST` are useful to differentiate
+ * since renaming callbacks may break Python scripts.
+ *
+ * Callbacks for Common Actions
+ * ============================
+ *
+ * - `{ACTION}_PRE` run before the action.
+ * - `{ACTION}_POST` run after the action.
+ *
+ * Optional Additional Callbacks
+ * -----------------------------
+ *
+ * - `{ACTION}_INIT` when the handler may manipulate the context used to run the action.
+ *
+ * Examples where `INIT` functions may be useful are:
+ *
+ * - When rendering, an `INIT` function may change the camera or render settings,
+ * things which a `PRE` function can't support as this information has already been used.
+ * - When saving an `INIT` function could temporarily change the preferences.
+ *
+ * - `{ACTION}_POST_FAIL` should be included if the action may fail.
+ *
+ * Use this so a call to the `PRE` callback always has a matching call to `POST` or `POST_FAIL`.
+ *
+ * \note in most cases only `PRE/POST` are required.
+ *
+ * Callbacks for Background/Modal Tasks
+ * ====================================
+ *
+ * - `{ACTION}_INIT`
+ * - `{ACTION}_COMPLETE` when a background job has finished.
+ * - `{ACTION}_CANCEL` When a background job is canceled partway through.
+ *
+ * While cancellation may be caused by any number of reasons, common causes may include:
+ *
+ * - Explicit user cancellation.
+ * - Exiting Blender.
+ * - Failure to acquire resources (such as disk-full, out of memory ... etc).
+ *
+ * \note `PRE/POST` handlers may be used along side modal task handlers
+ * as is the case for rendering, where rendering an animation uses modal task handlers,
+ * rendering a single frame has `PRE/POST` handlers.
+ *
+ * Python Access
+ * =============
+ *
+ * All callbacks here must be exposed via the Python module `bpy.app.handlers`,
+ * see `bpy_app_handlers.c`.
*/
typedef enum {
BKE_CB_EVT_FRAME_CHANGE_PRE,
@@ -59,6 +108,7 @@ typedef enum {
BKE_CB_EVT_VERSION_UPDATE,
BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST,
BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST,
+ BKE_CB_EVT_XR_SESSION_START_PRE,
BKE_CB_EVT_TOT,
} eCbEvent;
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index d0fca5e3796..7963d54126e 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -88,7 +88,7 @@ struct Collection *BKE_collection_object_find(struct Main *bmain,
struct Scene *scene,
struct Collection *collection,
struct Object *ob);
-bool BKE_collection_is_empty(struct Collection *collection);
+bool BKE_collection_is_empty(const struct Collection *collection);
bool BKE_collection_object_add(struct Main *bmain,
struct Collection *collection,
@@ -112,7 +112,9 @@ bool BKE_scene_collections_object_remove(struct Main *bmain,
struct Object *object,
const bool free_us);
void BKE_collections_object_remove_nulls(struct Main *bmain);
-void BKE_collections_child_remove_nulls(struct Main *bmain, struct Collection *old_collection);
+void BKE_collections_child_remove_nulls(struct Main *bmain,
+ struct Collection *parent_collection,
+ struct Collection *child_collection);
/* Dependencies. */
@@ -227,6 +229,8 @@ void BKE_scene_objects_iterator_begin(struct BLI_Iterator *iter, void *data_in);
void BKE_scene_objects_iterator_next(struct BLI_Iterator *iter);
void BKE_scene_objects_iterator_end(struct BLI_Iterator *iter);
+struct GSet *BKE_scene_objects_as_gset(struct Scene *scene, struct GSet *objects_gset);
+
#define FOREACH_SCENE_COLLECTION_BEGIN(scene, _instance) \
ITER_BEGIN (BKE_scene_collections_iterator_begin, \
BKE_scene_collections_iterator_next, \
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 3d30188e517..50aa6027840 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area);
void CTX_wm_region_set(bContext *C, struct ARegion *region);
void CTX_wm_menu_set(bContext *C, struct ARegion *menu);
void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup);
-const char *CTX_wm_operator_poll_msg_get(struct bContext *C);
+
+/**
+ * Values to create the message that describes the reason poll failed.
+ *
+ * \note This must be called in the same context as the poll function that created it.
+ */
+struct bContextPollMsgDyn_Params {
+ /** The result is allocated . */
+ char *(*get_fn)(bContext *C, void *user_data);
+ /** Optionally free the user-data. */
+ void (*free_fn)(bContext *C, void *user_data);
+ void *user_data;
+};
+
+const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free);
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg);
+void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
+ const struct bContextPollMsgDyn_Params *params);
+void CTX_wm_operator_poll_msg_clear(struct bContext *C);
/* Data Context
*
diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h
index 660e7c08062..eb9c68f80ec 100644
--- a/source/blender/blenkernel/BKE_curve.h
+++ b/source/blender/blenkernel/BKE_curve.h
@@ -40,7 +40,6 @@ struct MDeformVert;
struct Main;
struct Nurb;
struct Object;
-struct Path;
struct TextBox;
struct rctf;
@@ -50,7 +49,13 @@ typedef struct CurveCache {
ListBase disp;
ListBase bev;
ListBase deformed_nurbs;
- struct Path *path;
+ /* This array contains the accumulative length of the curve segments.
+ * So you can see this as a "total distance traveled" along the curve.
+ * The first entry is the length between point 0 and 1 while the last is the
+ * total length of the curve.
+ *
+ * Used by #BKE_where_on_path. */
+ const float *anim_path_accum_length;
} CurveCache;
/* Definitions needed for shape keys */
@@ -69,16 +74,16 @@ typedef struct CVKeyIndex {
#define SEGMENTSU(nu) (((nu)->flagu & CU_NURB_CYCLIC) ? (nu)->pntsu : (nu)->pntsu - 1)
#define SEGMENTSV(nu) (((nu)->flagv & CU_NURB_CYCLIC) ? (nu)->pntsv : (nu)->pntsv - 1)
-#define CU_DO_TILT(cu, nu) ((((nu)->flag & CU_2D) && ((cu)->flag & CU_3D) == 0) ? 0 : 1)
#define CU_DO_RADIUS(cu, nu) \
- ((CU_DO_TILT(cu, nu) || ((cu)->flag & CU_PATH_RADIUS) || (cu)->bevobj || (cu)->ext1 != 0.0f || \
+ ((((cu)->flag & (CU_PATH_RADIUS | CU_3D)) || (cu)->bevobj || (cu)->ext1 != 0.0f || \
(cu)->ext2 != 0.0f) ? \
1 : \
0)
+#define CU_IS_2D(cu) (((cu)->flag & CU_3D) == 0)
+
/* not 3d and not unfilled */
-#define CU_DO_2DFILL(cu) \
- ((((cu)->flag & CU_3D) == 0) && (((cu)->flag & (CU_FRONT | CU_BACK)) != 0))
+#define CU_DO_2DFILL(cu) (CU_IS_2D(cu) && (((cu)->flag & (CU_FRONT | CU_BACK)) != 0))
/* ** Curve ** */
void BKE_curve_editfont_free(struct Curve *cu);
@@ -86,7 +91,7 @@ void BKE_curve_init(struct Curve *cu, const short curve_type);
struct Curve *BKE_curve_add(struct Main *bmain, const char *name, int type);
short BKE_curve_type_get(const struct Curve *cu);
void BKE_curve_type_test(struct Object *ob);
-void BKE_curve_curve_dimension_update(struct Curve *cu);
+void BKE_curve_dimension_update(struct Curve *cu);
struct BoundBox *BKE_curve_boundbox_get(struct Object *ob);
@@ -108,7 +113,7 @@ void BKE_curve_transform(struct Curve *cu,
const bool do_props);
void BKE_curve_translate(struct Curve *cu, const float offset[3], const bool do_keys);
void BKE_curve_material_index_remove(struct Curve *cu, int index);
-bool BKE_curve_material_index_used(struct Curve *cu, int index);
+bool BKE_curve_material_index_used(const struct Curve *cu, int index);
void BKE_curve_material_index_clear(struct Curve *cu);
bool BKE_curve_material_index_validate(struct Curve *cu);
void BKE_curve_material_remap(struct Curve *cu, const unsigned int *remap, unsigned int remap_len);
@@ -125,8 +130,10 @@ void BKE_curve_nurb_vert_active_set(struct Curve *cu, const struct Nurb *nu, con
bool BKE_curve_nurb_vert_active_get(struct Curve *cu, struct Nurb **r_nu, void **r_vert);
void BKE_curve_nurb_vert_active_validate(struct Curve *cu);
-float (*BKE_curve_nurbs_vert_coords_alloc(struct ListBase *lb, int *r_vert_len))[3];
-void BKE_curve_nurbs_vert_coords_get(struct ListBase *lb, float (*vert_coords)[3], int vert_len);
+float (*BKE_curve_nurbs_vert_coords_alloc(const struct ListBase *lb, int *r_vert_len))[3];
+void BKE_curve_nurbs_vert_coords_get(const struct ListBase *lb,
+ float (*vert_coords)[3],
+ int vert_len);
void BKE_curve_nurbs_vert_coords_apply_with_mat4(struct ListBase *lb,
const float (*vert_coords)[3],
@@ -137,7 +144,7 @@ void BKE_curve_nurbs_vert_coords_apply(struct ListBase *lb,
const float (*vert_coords)[3],
const bool constrain_2d);
-float (*BKE_curve_nurbs_key_vert_coords_alloc(struct ListBase *lb,
+float (*BKE_curve_nurbs_key_vert_coords_alloc(const struct ListBase *lb,
float *key,
int *r_vert_len))[3];
void BKE_curve_nurbs_key_vert_tilts_apply(struct ListBase *lb, const float *key);
@@ -149,7 +156,7 @@ struct ListBase *BKE_curve_editNurbs_get(struct Curve *cu);
void BKE_curve_bevelList_free(struct ListBase *bev);
void BKE_curve_bevelList_make(struct Object *ob, struct ListBase *nurbs, bool for_render);
-void BKE_curve_bevel_make(struct Object *ob, struct ListBase *disp);
+ListBase BKE_curve_bevel_make(const struct Curve *ob);
void BKE_curve_forward_diff_bezier(
float q0, float q1, float q2, float q3, float *p, int it, int stride);
@@ -166,8 +173,8 @@ void BKE_curve_correct_bezpart(const float v1[2], float v2[2], float v3[2], cons
bool BKE_nurbList_index_get_co(struct ListBase *editnurb, const int index, float r_co[3]);
-int BKE_nurbList_verts_count(struct ListBase *nurb);
-int BKE_nurbList_verts_count_without_handles(struct ListBase *nurb);
+int BKE_nurbList_verts_count(const struct ListBase *nurb);
+int BKE_nurbList_verts_count_without_handles(const struct ListBase *nurb);
void BKE_nurbList_free(struct ListBase *lb);
void BKE_nurbList_duplicate(struct ListBase *lb1, const struct ListBase *lb2);
@@ -184,8 +191,8 @@ void BKE_nurb_free(struct Nurb *nu);
struct Nurb *BKE_nurb_duplicate(const struct Nurb *nu);
struct Nurb *BKE_nurb_copy(struct Nurb *src, int pntsu, int pntsv);
-void BKE_nurb_test_2d(struct Nurb *nu);
-void BKE_nurb_minmax(struct Nurb *nu, bool use_radius, float min[3], float max[3]);
+void BKE_nurb_project_2d(struct Nurb *nu);
+void BKE_nurb_minmax(const struct Nurb *nu, bool use_radius, float min[3], float max[3]);
float BKE_nurb_calc_length(const struct Nurb *nu, int resolution);
void BKE_nurb_makeFaces(
diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h
index 05e60d38487..a2d9bbcd011 100644
--- a/source/blender/blenkernel/BKE_displist.h
+++ b/source/blender/blenkernel/BKE_displist.h
@@ -65,47 +65,36 @@ struct Mesh;
struct Object;
struct Scene;
-/* used for curves, nurbs, mball, importing */
+/* Used for curves, nurbs, meta-balls. */
typedef struct DispList {
struct DispList *next, *prev;
short type, flag;
int parts, nr;
- short col, rt; /* rt used by initrenderNurbs */
+ short col, rt; /* Currently only used for smooth flag. */
float *verts, *nors;
int *index;
int charidx;
int totindex; /* indexed array drawing surfaces */
-
- unsigned int *bevel_split; /* BLI_bitmap */
} DispList;
-void BKE_displist_copy(struct ListBase *lbn, struct ListBase *lb);
-void BKE_displist_elem_free(DispList *dl);
-DispList *BKE_displist_find_or_create(struct ListBase *lb, int type);
+void BKE_displist_copy(struct ListBase *lbn, const struct ListBase *lb);
DispList *BKE_displist_find(struct ListBase *lb, int type);
void BKE_displist_normals_add(struct ListBase *lb);
-void BKE_displist_count(struct ListBase *lb, int *totvert, int *totface, int *tottri);
+void BKE_displist_count(const struct ListBase *lb, int *totvert, int *totface, int *tottri);
void BKE_displist_free(struct ListBase *lb);
-bool BKE_displist_has_faces(struct ListBase *lb);
-
-void BKE_displist_make_surf(struct Depsgraph *depsgraph,
- struct Scene *scene,
- struct Object *ob,
- struct ListBase *dispbase,
- struct Mesh **r_final,
- const bool for_render,
- const bool for_orco);
+bool BKE_displist_has_faces(const struct ListBase *lb);
+
void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph,
- struct Scene *scene,
+ const struct Scene *scene,
struct Object *ob,
const bool for_render,
const bool for_orco);
void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph,
- struct Scene *scene,
+ const struct Scene *scene,
struct Object *ob,
struct ListBase *dispbase,
- struct Mesh **r_final,
- const bool for_orco);
+ const bool for_orco,
+ struct Mesh **r_final);
void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph,
struct Scene *scene,
@@ -113,22 +102,26 @@ void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph,
struct ListBase *dispbase);
bool BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph,
- struct Scene *scene,
+ const struct Scene *scene,
struct Object *ob,
struct ListBase *source_nurb,
struct ListBase *target_nurb,
const bool for_render);
+bool BKE_displist_surfindex_get(
+ const struct DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4);
-bool BKE_displist_surfindex_get(DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4);
void BKE_displist_fill(const struct ListBase *dispbase,
struct ListBase *to,
const float normal_proj[3],
const bool flip_normal);
-float BKE_displist_calc_taper(
- struct Depsgraph *depsgraph, struct Scene *scene, struct Object *taperobj, int cur, int tot);
+float BKE_displist_calc_taper(struct Depsgraph *depsgraph,
+ const struct Scene *scene,
+ struct Object *taperobj,
+ int cur,
+ int tot);
-void BKE_displist_minmax(struct ListBase *dispbase, float min[3], float max[3]);
+void BKE_displist_minmax(const struct ListBase *dispbase, float min[3], float max[3]);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h
index 2fb713a4299..3a1eedfd807 100644
--- a/source/blender/blenkernel/BKE_editmesh.h
+++ b/source/blender/blenkernel/BKE_editmesh.h
@@ -33,6 +33,7 @@ extern "C" {
struct BMLoop;
struct BMesh;
+struct BMPartialUpdate;
struct BoundBox;
struct Depsgraph;
struct Mesh;
@@ -85,6 +86,8 @@ typedef struct BMEditMesh {
/* editmesh.c */
void BKE_editmesh_looptri_calc(BMEditMesh *em);
+void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo);
+
BMEditMesh *BKE_editmesh_create(BMesh *bm, const bool do_tessellate);
BMEditMesh *BKE_editmesh_copy(BMEditMesh *em);
BMEditMesh *BKE_editmesh_from_object(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index 4569e68ea4a..589d1839dd4 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -232,6 +232,19 @@ int BKE_fcurve_bezt_binarysearch_index(struct BezTriple array[],
int arraylen,
bool *r_replace);
+/* fcurve_cache.c */
+/* Cached f-curve look-ups, use when this needs to be done many times. */
+struct FCurvePathCache;
+struct FCurvePathCache *BKE_fcurve_pathcache_create(ListBase *list);
+void BKE_fcurve_pathcache_destroy(struct FCurvePathCache *fcache);
+struct FCurve *BKE_fcurve_pathcache_find(struct FCurvePathCache *fcache,
+ const char rna_path[],
+ const int array_index);
+int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache,
+ const char *rna_path,
+ struct FCurve **fcurve_result,
+ int fcurve_result_len);
+
/* get the time extents for F-Curve */
bool BKE_fcurve_calc_range(
struct FCurve *fcu, float *min, float *max, const bool do_sel_only, const bool do_min_length);
@@ -245,6 +258,14 @@ bool BKE_fcurve_calc_bounds(struct FCurve *fcu,
const bool do_sel_only,
const bool include_handles);
+float *BKE_fcurves_calc_keyed_frames_ex(struct FCurve **fcurve_array,
+ const int fcurve_array_len,
+ const float interval,
+ int *r_frames_len);
+float *BKE_fcurves_calc_keyed_frames(struct FCurve **fcurve_array,
+ const int fcurve_array_len,
+ int *r_frames_len);
+
void BKE_fcurve_active_keyframe_set(struct FCurve *fcu, const struct BezTriple *active_bezt);
int BKE_fcurve_active_keyframe_index(const struct FCurve *fcu);
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
index 08b4a25d946..5f6a9ec7b91 100644
--- a/source/blender/blenkernel/BKE_geometry_set.h
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -36,30 +36,13 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
GEO_COMPONENT_TYPE_INSTANCES = 2,
GEO_COMPONENT_TYPE_VOLUME = 3,
+ GEO_COMPONENT_TYPE_CURVE = 4,
} GeometryComponentType;
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_transforms)[4][4],
- const int **r_almost_unique_ids,
- struct InstancedData **r_instanced_data);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 8cc37a3e711..b2342a5fd96 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -25,11 +25,11 @@
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
-#include "BLI_function_ref.hh"
#include "BLI_hash.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_user_counter.hh"
+#include "BLI_vector_set.hh"
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
@@ -39,6 +39,8 @@ struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
+struct Curve;
+struct CurveEval;
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
@@ -56,74 +58,6 @@ class ComponentAttributeProviders;
class GeometryComponent;
/**
- * An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final
- * destination yet. Therefore, once the attribute has been filled with data, the #save method has
- * to be called, to store the attribute where it belongs (possibly by replacing an existing
- * attribute with the same name).
- *
- * This is useful for example in the Attribute Color Ramp node, when the same attribute name is
- * used as input and output. Typically the input is a float attribute, and the output is a color.
- * Those two attributes cannot exist at the same time, due to a name collision. To handle this
- * situation well, first the output colors have to be computed before the input floats are deleted.
- * Therefore, the outputs have to be written to a temporary buffer that replaces the existing
- * attribute once all computations are done.
- */
-class OutputAttributePtr {
- private:
- blender::bke::WriteAttributePtr attribute_;
-
- public:
- OutputAttributePtr() = default;
- OutputAttributePtr(blender::bke::WriteAttributePtr attribute);
- OutputAttributePtr(GeometryComponent &component,
- AttributeDomain domain,
- std::string name,
- CustomDataType data_type);
-
- ~OutputAttributePtr();
-
- /* Returns false, when this wrapper is empty. */
- operator bool() const
- {
- return static_cast<bool>(attribute_);
- }
-
- /* Get a reference to the underlying #WriteAttribute. */
- blender::bke::WriteAttribute &get()
- {
- BLI_assert(attribute_);
- return *attribute_;
- }
-
- blender::bke::WriteAttribute &operator*()
- {
- return *attribute_;
- }
-
- blender::bke::WriteAttribute *operator->()
- {
- return attribute_.get();
- }
-
- void save();
- void apply_span_and_save();
-};
-
-/**
- * Contains information about an attribute in a geometry component.
- * More information can be added in the future. E.g. whether the attribute is builtin and how it is
- * stored (uv map, vertex group, ...).
- */
-struct AttributeMetaData {
- AttributeDomain domain;
- CustomDataType data_type;
-};
-
-/* Returns false when the iteration should be stopped. */
-using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
- const AttributeMetaData &meta_data)>;
-
-/**
* This is the base class for specialized geometry component types.
*/
class GeometryComponent {
@@ -135,12 +69,18 @@ class GeometryComponent {
public:
GeometryComponent(GeometryComponentType type);
- virtual ~GeometryComponent();
+ virtual ~GeometryComponent() = default;
static GeometryComponent *create(GeometryComponentType component_type);
/* The returned component should be of the same type as the type this is called on. */
virtual GeometryComponent *copy() const = 0;
+ /* Direct data is everything except for instances of objects/collections.
+ * If this returns true, the geometry set can be cached and is still valid after e.g. modifier
+ * evaluation ends. Instances can only be valid as long as the data they instance is valid. */
+ virtual bool owns_direct_data() const = 0;
+ virtual void ensure_owns_direct_data() = 0;
+
void user_add() const;
void user_remove() const;
bool is_mutable() const;
@@ -150,26 +90,34 @@ class GeometryComponent {
/* Return true when any attribute with this name exists, including built in attributes. */
bool attribute_exists(const blender::StringRef attribute_name) const;
+ /* Return the data type and domain of an attribute with the given name if it exists. */
+ std::optional<AttributeMetaData> attribute_get_meta_data(
+ const blender::StringRef attribute_name) const;
+
/* Returns true when the geometry component supports this attribute domain. */
bool attribute_domain_supported(const AttributeDomain domain) const;
/* Can only be used with supported domain types. */
virtual int attribute_domain_size(const AttributeDomain domain) const;
+ bool attribute_is_builtin(const blender::StringRef attribute_name) const;
+
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name) const;
/* Get read and write access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- blender::bke::WriteAttributePtr attribute_try_get_for_write(
+ blender::bke::WriteAttributeLookup attribute_try_get_for_write(
const blender::StringRef attribute_name);
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
* interpolate from one domain to another.
* Returns null if the interpolation is not implemented. */
- virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
- blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const;
+ virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const;
/* Returns true when the attribute has been deleted. */
bool attribute_try_delete(const blender::StringRef attribute_name);
@@ -177,82 +125,104 @@ class GeometryComponent {
/* Returns true when the attribute has been created. */
bool attribute_try_create(const blender::StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type);
+ const CustomDataType data_type,
+ const AttributeInit &initializer);
+
+ /* Try to create the builtin attribute with the given name. No data type or domain has to be
+ * provided, because those are fixed for builtin attributes. */
+ bool attribute_try_create_builtin(const blender::StringRef attribute_name,
+ const AttributeInit &initializer);
blender::Set<std::string> attribute_names() const;
- void attribute_foreach(const AttributeForeachCallback callback) const;
+ bool attribute_foreach(const AttributeForeachCallback callback) const;
virtual bool is_empty() const;
- /* Get a read-only attribute for the given domain and data type.
- * Returns null when it does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ /* Get a virtual array to read the data of an attribute on the given domain and data type.
+ * Returns null when the attribute does not exist or cannot be converted to the requested domain
+ * and data type. */
+ std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
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(
+ /* Get a virtual array to read the data of an attribute on the given domain. The data type is
+ * left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
+ * requested domain. */
+ std::unique_ptr<blender::fn::GVArray> 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. */
- blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value) const;
+ /* Get a virtual array to read data of an attribute with the given data type. The domain is
+ * left unchanged. Returns null when the attribute does not exist or cannot be converted to the
+ * requested data type. */
+ blender::bke::ReadAttributeLookup attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const CustomDataType data_type) const;
- /* Get a typed read-only attribute for the given domain and type. */
- template<typename T>
- blender::bke::TypedReadAttribute<T> attribute_get_for_read(
+ /* Get a virtual array to read the data of an attribute. If that is not possible, the returned
+ * virtual array will contain a default value. This never returns null. */
+ std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
- const T &default_value) const
+ const CustomDataType data_type,
+ const void *default_value = nullptr) const;
+
+ /* Should be used instead of the method above when the requested data type is known at compile
+ * time for better type safety. */
+ template<typename T>
+ blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
- return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
+ std::unique_ptr varray = this->attribute_get_for_read(
+ attribute_name, domain, type, &default_value);
+ return blender::fn::GVArray_Typed<T>(std::move(varray));
}
- /* Get a read-only dummy attribute that always returns the same value. */
- blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
- const CustomDataType data_type,
- const void *value) const;
+ /**
+ * Returns an "output attribute", which is essentially a mutable virtual array with some commonly
+ * used convince features. The returned output attribute might be empty if requested attribute
+ * cannot exist on the geometry.
+ *
+ * The included convenience features are:
+ * - Implicit type conversion when writing to builtin attributes.
+ * - If the attribute name exists already, but has a different type/domain, a temporary attribute
+ * is created that will overwrite the existing attribute in the end.
+ */
+ blender::bke::OutputAttribute attribute_try_get_for_output(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value = nullptr);
- /* Create a read-only dummy attribute that always returns the same value.
- * The given value is converted to the correct type if necessary. */
- blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted(
+ /* Same as attribute_try_get_for_output, but should be used when the original values in the
+ * attributes are not read, i.e. the attribute is used only for output. Since values are not read
+ * from this attribute, no default value is necessary. */
+ blender::bke::OutputAttribute attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType in_data_type,
- const CustomDataType out_data_type,
- const void *value) const;
+ const CustomDataType data_type);
- /* Get a read-only dummy attribute that always returns the same value. */
+ /* Statically typed method corresponding to the equally named generic one. */
template<typename T>
- blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
- const T &value) const
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
+ const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
- const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
- return this->attribute_get_constant_for_read(domain, type, &value);
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value);
}
- /**
- * If an attribute with the given params exist, it is returned.
- * If no attribute with the given name exists, create it and
- * fill it with the default value if it is provided.
- * If an attribute with the given name but different domain or type exists, a temporary attribute
- * is created that has to be saved after the output has been computed. This avoids deleting
- * another attribute, before a computation is finished.
- *
- * This might return no attribute when the attribute cannot exist on the component.
- */
- OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value = nullptr);
+ /* Statically typed method corresponding to the equally named generic one. */
+ template<typename T>
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name, const AttributeDomain domain)
+ {
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
+ }
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
@@ -313,28 +283,43 @@ struct GeometrySet {
friend bool operator==(const GeometrySet &a, const GeometrySet &b);
uint64_t hash() const;
+ void clear();
+
+ void ensure_owns_direct_data();
+
/* Utility methods for creation. */
static GeometrySet create_with_mesh(
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ static GeometrySet create_with_curve(
+ CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
bool has_volume() const;
+ bool has_curve() const;
+
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
const Volume *get_volume_for_read() const;
+ const CurveEval *get_curve_for_read() const;
+
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
Volume *get_volume_for_write();
+ CurveEval *get_curve_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_volume(Volume *volume,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_curve(CurveEval *curve,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
};
/** A geometry component that can store a mesh. */
@@ -367,11 +352,16 @@ class MeshComponent : public GeometryComponent {
Mesh *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
- blender::bke::ReadAttributePtr attribute_try_adapt_domain(
- blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final;
+ std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const final;
bool is_empty() const final;
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_MESH;
private:
@@ -402,18 +392,137 @@ class PointCloudComponent : public GeometryComponent {
bool is_empty() const final;
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
+/** A geometry component that stores curve data, in other words, a group of splines. */
+class CurveComponent : public GeometryComponent {
+ private:
+ CurveEval *curve_ = nullptr;
+ GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+
+ /**
+ * Curve data necessary to hold the draw cache for rendering, consistent over multiple redraws.
+ * This is necessary because Blender assumes that objects evaluate to an object data type, and
+ * we use #CurveEval rather than #Curve here. It also allows us to mostly reuse the same
+ * batch cache implementation.
+ */
+ mutable Curve *curve_for_render_ = nullptr;
+ mutable std::mutex curve_for_render_mutex_;
+
+ public:
+ CurveComponent();
+ ~CurveComponent();
+ GeometryComponent *copy() const override;
+
+ void clear();
+ bool has_curve() const;
+ void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ CurveEval *release();
+
+ const CurveEval *get_for_read() const;
+ CurveEval *get_for_write();
+
+ int attribute_domain_size(const AttributeDomain domain) const final;
+ std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const final;
+
+ bool is_empty() const final;
+
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
+ const Curve *get_curve_for_render() const;
+
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
+
+ private:
+ const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
+};
+
+class InstanceReference {
+ public:
+ enum class Type {
+ /**
+ * An empty instance. This allows an `InstanceReference` to be default constructed without
+ * being in an invalid state. There might also be other use cases that we haven't explored much
+ * yet (such as changing the instance later on, and "disabling" some instances).
+ */
+ None,
+ Object,
+ Collection,
+ };
+
+ private:
+ Type type_ = Type::None;
+ /** Depending on the type this is either null, an Object or Collection pointer. */
+ void *data_ = nullptr;
+
+ public:
+ InstanceReference() = default;
+
+ InstanceReference(Object &object) : type_(Type::Object), data_(&object)
+ {
+ }
+
+ InstanceReference(Collection &collection) : type_(Type::Collection), data_(&collection)
+ {
+ }
+
+ Type type() const
+ {
+ return type_;
+ }
+
+ Object &object() const
+ {
+ BLI_assert(type_ == Type::Object);
+ return *(Object *)data_;
+ }
+
+ Collection &collection() const
+ {
+ BLI_assert(type_ == Type::Collection);
+ return *(Collection *)data_;
+ }
+
+ uint64_t hash() const
+ {
+ return blender::get_default_hash(data_);
+ }
+
+ friend bool operator==(const InstanceReference &a, const InstanceReference &b)
+ {
+ return a.data_ == b.data_;
+ }
+};
+
/** A geometry component that stores instances. */
class InstancesComponent : public GeometryComponent {
private:
- blender::Vector<blender::float4x4> transforms_;
- blender::Vector<int> ids_;
- blender::Vector<InstancedData> instanced_data_;
+ /**
+ * Indexed set containing information about the data that is instanced.
+ * Actual instances store an index ("handle") into this set.
+ */
+ blender::VectorSet<InstanceReference> references_;
+
+ /** Index into `references_`. Determines what data is instanced. */
+ blender::Vector<int> instance_reference_handles_;
+ /** Transformation of the instances. */
+ blender::Vector<blender::float4x4> instance_transforms_;
+ /**
+ * IDs of the instances. They are used for consistency over multiple frames for things like
+ * motion blur.
+ */
+ blender::Vector<int> instance_ids_;
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
* all. They are *almost* unique, because under certain very unlikely circumstances, they are not
@@ -428,20 +537,31 @@ class InstancesComponent : public GeometryComponent {
GeometryComponent *copy() const override;
void clear();
- void add_instance(Object *object, blender::float4x4 transform, const int id = -1);
- void add_instance(Collection *collection, blender::float4x4 transform, const int id = -1);
- void add_instance(InstancedData data, blender::float4x4 transform, const int id = -1);
-
- blender::Span<InstancedData> instanced_data() const;
- blender::Span<blender::float4x4> transforms() const;
- blender::Span<int> ids() const;
- blender::MutableSpan<blender::float4x4> transforms();
+
+ void reserve(int min_capacity);
+ void resize(int capacity);
+
+ int add_reference(InstanceReference reference);
+ void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1);
+
+ blender::Span<InstanceReference> references() const;
+
+ blender::Span<int> instance_reference_handles() const;
+ blender::MutableSpan<int> instance_reference_handles();
+ blender::MutableSpan<blender::float4x4> instance_transforms();
+ blender::Span<blender::float4x4> instance_transforms() const;
+ blender::MutableSpan<int> instance_ids();
+ blender::Span<int> instance_ids() const;
+
int instances_amount() const;
blender::Span<int> almost_unique_ids() const;
bool is_empty() const final;
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES;
};
@@ -464,5 +584,8 @@ class VolumeComponent : public GeometryComponent {
const Volume *get_for_read() const;
Volume *get_for_write();
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME;
};
diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh
index 16c28e32e3c..25876296a47 100644
--- a/source/blender/blenkernel/BKE_geometry_set_instances.hh
+++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh
@@ -39,7 +39,12 @@ struct GeometryInstanceGroup {
Vector<float4x4> transforms;
};
-Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &geometry_set);
+void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
+ const AttributeForeachCallback callback,
+ const int limit);
+
+void geometry_set_gather_instances(const GeometrySet &geometry_set,
+ Vector<GeometryInstanceGroup> &r_instance_groups);
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set);
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set);
@@ -54,9 +59,9 @@ struct AttributeKind {
* will contain the highest complexity data type and the highest priority domain among every
* attribute with the given name on all of the input components.
*/
-void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
- Span<GeometryComponentType> component_types,
- Span<bke::GeometryInstanceGroup> set_groups,
- const Set<std::string> &ignored_attributes);
+void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
+ Span<GeometryComponentType> component_types,
+ const Set<std::string> &ignored_attributes,
+ Map<std::string, AttributeKind> &r_attributes);
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 9e237679795..74f2bf7c6ad 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -199,20 +199,6 @@ enum {
*/
#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI | G_FILE_RECOVER_READ | G_FILE_RECOVER_WRITE)
-/** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */
-#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
-# error Either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ must be defined.
-#endif
-
-#define L_ENDIAN 1
-#define B_ENDIAN 0
-
-#ifdef __BIG_ENDIAN__
-# define ENDIAN_ORDER B_ENDIAN
-#else
-# define ENDIAN_ORDER L_ENDIAN
-#endif
-
/** #Global.moving, signals drawing in (3d) window to denote transform */
enum {
G_TRANSFORM_OBJ = (1 << 0),
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 4b4886e8bf3..bb145580928 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -108,7 +108,10 @@ void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps);
struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
-struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive);
+struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd,
+ const char *name,
+ const bool setactive,
+ const bool add_to_header);
struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]);
struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src,
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index a9bd0a524c4..8fc3ce133a0 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -111,7 +111,10 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd,
struct bGPDstroke *gps,
const short tag);
-bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float tip_length);
+bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps,
+ const float dist,
+ const float overshoot_fac,
+ const short mode);
bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
const int index_from,
const int index_to);
@@ -135,7 +138,7 @@ bool BKE_gpencil_stroke_split(struct bGPdata *gpd,
struct bGPDstroke *gps,
const int before_index,
struct bGPDstroke **remaining_gps);
-bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist);
+bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist, const short mode);
float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
float BKE_gpencil_stroke_segment_length(const struct bGPDstroke *gps,
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index c51a5f7e5e1..d298e5dcf6d 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -325,6 +325,7 @@ int BKE_image_get_tile_from_pos(struct Image *ima,
const float uv[2],
float r_uv[2],
float r_ofs[2]);
+int BKE_image_find_nearest_tile(const struct Image *image, const float co[2]);
void BKE_image_get_size(struct Image *image, struct ImageUser *iuser, int *r_width, int *r_height);
void BKE_image_get_size_fl(struct Image *image, struct ImageUser *iuser, float r_size[2]);
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index 5a8d36b94ec..e16507bf3cc 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -104,6 +104,10 @@ enum {
* specific code in some copy cases (mostly for node trees). */
LIB_ID_CREATE_LOCAL = 1 << 9,
+ /** Create for the depsgraph, when set #LIB_TAG_COPIED_ON_WRITE must be set.
+ * Internally this is used to share some pointers instead of duplicating them. */
+ LIB_ID_COPY_SET_COPIED_ON_WRITE = 1 << 10,
+
/* *** Specific options to some ID types or usages. *** */
/* *** May be ignored by unrelated ID copying functions. *** */
/** Object only, needed by make_local code. */
@@ -116,6 +120,8 @@ enum {
LIB_ID_COPY_NO_ANIMDATA = 1 << 19,
/** Mesh: Reference CD data layers instead of doing real copy - USE WITH CAUTION! */
LIB_ID_COPY_CD_REFERENCE = 1 << 20,
+ /** Do not copy id->override_library, used by ID datablock override routines. */
+ LIB_ID_COPY_NO_LIB_OVERRIDE = 1 << 21,
/* *** XXX Hackish/not-so-nice specific behaviors needed for some corner cases. *** */
/* *** Ideally we should not have those, but we need them for now... *** */
@@ -136,7 +142,8 @@ enum {
LIB_ID_CREATE_LOCALIZE = LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_DEG_TAG,
/** Generate a local copy, outside of bmain, to work on (used by COW e.g.). */
- LIB_ID_COPY_LOCALIZE = LIB_ID_CREATE_LOCALIZE | LIB_ID_COPY_NO_PREVIEW | LIB_ID_COPY_CACHES,
+ LIB_ID_COPY_LOCALIZE = LIB_ID_CREATE_LOCALIZE | LIB_ID_COPY_NO_PREVIEW | LIB_ID_COPY_CACHES |
+ LIB_ID_COPY_NO_LIB_OVERRIDE,
};
void BKE_libblock_copy_ex(struct Main *bmain,
@@ -251,8 +258,10 @@ void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b);
void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint);
void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id);
-bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name)
- ATTR_NONNULL(1, 2);
+bool BKE_id_new_name_validate(struct ListBase *lb,
+ struct ID *id,
+ const char *name,
+ const bool do_linked_data) ATTR_NONNULL(1, 2);
void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id);
/* Affect whole Main database. */
@@ -266,7 +275,7 @@ void BKE_main_id_tag_all(struct Main *mainvar, const int tag, const bool value);
void BKE_main_id_flag_listbase(struct ListBase *lb, const int flag, const bool value);
void BKE_main_id_flag_all(struct Main *bmain, const int flag, const bool value);
-void BKE_main_id_clear_newpoins(struct Main *bmain);
+void BKE_main_id_newptr_and_tag_clear(struct Main *bmain);
void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_only);
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index f69580d38be..4dc99e64cf2 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -47,6 +47,7 @@ struct ID;
struct IDOverrideLibrary;
struct IDOverrideLibraryProperty;
struct IDOverrideLibraryPropertyOperation;
+struct Library;
struct Main;
struct Object;
struct PointerRNA;
@@ -68,12 +69,16 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id);
struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
struct ID *reference_id,
const bool do_tagged_remap);
-bool BKE_lib_override_library_create_from_tag(struct Main *bmain);
+bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
+ const struct Library *reference_library,
+ const bool do_no_main);
bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct ID *id_root,
- struct ID *id_reference);
+ struct ID *id_reference,
+ struct ID **r_id_root_override);
+bool BKE_lib_override_library_template_create(struct ID *id);
bool BKE_lib_override_library_proxy_convert(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
@@ -83,10 +88,13 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
struct ViewLayer *view_layer,
struct ID *id_root,
struct Collection *override_resync_residual_storage,
- const bool do_hierarchy_enforce);
+ const bool do_hierarchy_enforce,
+ const bool do_post_process,
+ struct ReportList *reports);
void BKE_lib_override_library_main_resync(struct Main *bmain,
struct Scene *scene,
- struct ViewLayer *view_layer);
+ struct ViewLayer *view_layer,
+ struct ReportList *reports);
void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root);
diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h
index 4e781aea9d3..9c49514e7b8 100644
--- a/source/blender/blenkernel/BKE_lib_query.h
+++ b/source/blender/blenkernel/BKE_lib_query.h
@@ -70,12 +70,15 @@ enum {
/** That ID is used as library override's reference by its owner. */
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 5),
+ /** That ID pointer is not overridable. */
+ IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 6),
+
/**
* Indicates that this is an internal runtime ID pointer, like e.g. `ID.newid` or `ID.original`.
* \note Those should be ignored in most cases, and won't be processed/generated anyway unless
* `IDWALK_DO_INTERNAL_RUNTIME_POINTERS` option is enabled.
*/
- IDWALK_CB_INTERNAL = (1 << 6),
+ IDWALK_CB_INTERNAL = (1 << 7),
/**
* This ID usage is fully refcounted.
diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index 705d2b030e5..e806dedc14c 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -85,6 +85,10 @@ enum {
* freed ones).
*/
ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 7,
+ /** Force handling user count even for IDs that are outside of Main (used in some cases when
+ * dealing with IDs temporarily out of Main, but which will be put in it ultimately).
+ */
+ ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8,
};
/* Note: Requiring new_id to be non-null, this *may* not be the case ultimately,
diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h
index 14ea50f808a..69e2d52e1dd 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -51,6 +51,9 @@ void BKE_object_material_remap(struct Object *ob, const unsigned int *remap);
void BKE_object_material_remap_calc(struct Object *ob_dst,
struct Object *ob_src,
short *remap_src_to_dst);
+void BKE_object_material_from_eval_data(struct Main *bmain,
+ struct Object *ob_orig,
+ struct ID *data_eval);
struct Material *BKE_material_add(struct Main *bmain, const char *name);
struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name);
void BKE_gpencil_material_attr_init(struct Material *ma);
@@ -105,6 +108,13 @@ struct Material *BKE_id_material_pop(struct Main *bmain,
/* index is an int because of RNA. */
int index);
void BKE_id_material_clear(struct Main *bmain, struct ID *id);
+
+/* eval api */
+struct Material *BKE_object_material_get_eval(struct Object *ob, short act);
+int BKE_object_material_count_eval(struct Object *ob);
+void BKE_id_material_eval_assign(struct ID *id, int slot, struct Material *material);
+void BKE_id_material_eval_ensure_default_slot(struct ID *id);
+
/* rendering */
void ramp_blend(int type, float r_col[3], const float fac, const float col[3]);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index e39caac7c36..62837c4f2a7 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -215,7 +215,8 @@ void BKE_mesh_split_faces(struct Mesh *mesh, bool free_loop_normals);
* ignored otherwise. */
struct Mesh *BKE_mesh_new_from_object(struct Depsgraph *depsgraph,
struct Object *object,
- bool preserve_all_data_layers);
+ const bool preserve_all_data_layers,
+ const bool preserve_origindex);
/* This is a version of BKE_mesh_new_from_object() which stores mesh in the given main database.
* However, that function enforces object type to be a geometry one, and ensures a mesh is always
@@ -577,9 +578,9 @@ void BKE_mesh_polygons_flip(struct MPoly *mpoly,
struct CustomData *ldata,
int totpoly);
-/* merge verts */
-/* Enum for merge_mode of CDDM_merge_verts.
- * Refer to mesh.c for details. */
+/* Merge verts. */
+/* Enum for merge_mode of #BKE_mesh_merge_verts.
+ * Refer to mesh_merge.c for details. */
enum {
MESH_MERGE_VERTS_DUMP_IF_MAPPED,
MESH_MERGE_VERTS_DUMP_IF_EQUAL,
diff --git a/source/blender/blenkernel/BKE_mesh_boolean_convert.hh b/source/blender/blenkernel/BKE_mesh_boolean_convert.hh
new file mode 100644
index 00000000000..59f6e75183e
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_boolean_convert.hh
@@ -0,0 +1,43 @@
+/*
+ * 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) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_array.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_mesh_boolean.hh"
+#include "BLI_span.hh"
+
+struct Mesh;
+
+namespace blender::meshintersect {
+
+Mesh *direct_mesh_boolean(blender::Span<const Mesh *> meshes,
+ blender::Span<const float4x4 *> obmats,
+ const float4x4 &target_transform,
+ blender::Span<blender::Array<short>> material_remaps,
+ const bool use_self,
+ const bool hole_tolerant,
+ const int boolean_mode);
+
+} // namespace blender::meshintersect
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
new file mode 100644
index 00000000000..f504650e349
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "FN_generic_virtual_array.hh"
+
+#include "BLI_float3.hh"
+
+#include "BKE_attribute.h"
+
+struct Mesh;
+
+namespace blender::bke::mesh_surface_sample {
+
+using fn::CPPType;
+using fn::GMutableSpan;
+using fn::GSpan;
+using fn::GVArray;
+
+void sample_point_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ Span<float3> bary_coords,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+void sample_corner_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ Span<float3> bary_coords,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+void sample_face_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index ab2ecbe2507..48b4540e3d9 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -258,10 +258,6 @@ typedef struct ModifierTypeInfo {
const struct ModifierEvalContext *ctx,
struct GeometrySet *geometry_set);
- struct Volume *(*modifyVolume)(struct ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct Volume *volume);
-
/********************* Optional functions *********************/
/**
@@ -450,8 +446,8 @@ bool BKE_modifier_is_preview(struct ModifierData *md);
void BKE_modifiers_foreach_ID_link(struct Object *ob, IDWalkFunc walk, void *userData);
void BKE_modifiers_foreach_tex_link(struct Object *ob, TexWalkFunc walk, void *userData);
-struct ModifierData *BKE_modifiers_findby_type(struct Object *ob, ModifierType type);
-struct ModifierData *BKE_modifiers_findby_name(struct Object *ob, const char *name);
+struct ModifierData *BKE_modifiers_findby_type(const struct Object *ob, ModifierType type);
+struct ModifierData *BKE_modifiers_findby_name(const struct Object *ob, const char *name);
void BKE_modifiers_clear_errors(struct Object *ob);
int BKE_modifiers_get_cage_index(const struct Scene *scene,
struct Object *ob,
diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h
index 16d48024d07..af238fda659 100644
--- a/source/blender/blenkernel/BKE_nla.h
+++ b/source/blender/blenkernel/BKE_nla.h
@@ -58,7 +58,14 @@ struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain,
struct NlaTrack *nlt,
const bool use_same_actions,
const int flag);
-void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, ListBase *src, const int flag);
+void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, const ListBase *src, const int flag);
+
+/* Copy NLA tracks from #adt_source to #adt_dest, and update the active track/strip pointers to
+ * point at those copies. */
+void BKE_nla_tracks_copy_from_adt(struct Main *bmain,
+ struct AnimData *adt_dest,
+ const struct AnimData *adt_source,
+ int flag);
struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
struct NlaTrack *prev,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 71f188a2de1..a67d7116874 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -289,10 +289,22 @@ typedef struct bNodeType {
void (*freefunc_api)(struct PointerRNA *ptr);
void (*copyfunc_api)(struct PointerRNA *ptr, const struct bNode *src_node);
- /* can this node type be added to a node tree */
- bool (*poll)(struct bNodeType *ntype, struct bNodeTree *nodetree);
- /* can this node be added to a node tree */
- bool (*poll_instance)(struct bNode *node, struct bNodeTree *nodetree);
+ /**
+ * Can this node type be added to a node tree?
+ * \param r_disabled_hint: Optional hint to display in the UI when the poll fails.
+ * The callback can set this to a static string without having to
+ * null-check it (or without setting it to null if it's not used).
+ * The caller must pass a valid `const char **` and null-initialize it
+ * when it's not just a dummy, that is, if it actually wants to access
+ * the returned disabled-hint (null-check needed!).
+ */
+ bool (*poll)(struct bNodeType *ntype, struct bNodeTree *nodetree, const char **r_disabled_hint);
+ /** Can this node be added to a node tree?
+ * \param r_disabled_hint: See `poll()`.
+ */
+ bool (*poll_instance)(struct bNode *node,
+ struct bNodeTree *nodetree,
+ const char **r_disabled_hint);
/* optional handling of link insertion */
void (*insert_link)(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
@@ -313,6 +325,7 @@ typedef struct bNodeType {
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
+ bool geometry_node_execute_supports_laziness;
/* RNA integration */
ExtensionRNA rna_ext;
@@ -398,6 +411,9 @@ typedef struct bNodeTreeType {
void (*node_add_init)(struct bNodeTree *ntree, struct bNode *bnode);
+ /* Check if the socket type is valid for this tree type. */
+ bool (*valid_socket_type)(enum eNodeSocketDatatype socket_type, struct bNodeTreeType *ntreetype);
+
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeTreeType;
@@ -483,14 +499,14 @@ void ntreeBlendReadExpand(struct BlendExpander *expander, struct bNodeTree *ntre
/** \name Node Tree Interface
* \{ */
struct bNodeSocket *ntreeFindSocketInterface(struct bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *identifier);
struct bNodeSocket *ntreeAddSocketInterface(struct bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *name);
struct bNodeSocket *ntreeInsertSocketInterface(struct bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
struct bNodeSocket *next_sock,
const char *name);
@@ -556,30 +572,32 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype);
} \
((void)0)
-struct bNodeSocket *nodeFindSocket(const struct bNode *node, int in_out, const char *identifier);
+struct bNodeSocket *nodeFindSocket(const struct bNode *node,
+ eNodeSocketInOut in_out,
+ const char *identifier);
struct bNodeSocket *nodeAddSocket(struct bNodeTree *ntree,
struct bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *identifier,
const char *name);
struct bNodeSocket *nodeInsertSocket(struct bNodeTree *ntree,
struct bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
struct bNodeSocket *next_sock,
const char *identifier,
const char *name);
struct bNodeSocket *nodeAddStaticSocket(struct bNodeTree *ntree,
struct bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
int type,
int subtype,
const char *identifier,
const char *name);
struct bNodeSocket *nodeInsertStaticSocket(struct bNodeTree *ntree,
struct bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
int type,
int subtype,
struct bNodeSocket *next_sock,
@@ -802,7 +820,9 @@ void BKE_node_preview_set_pixel(
void nodeLabel(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen);
const char *nodeSocketLabel(const struct bNodeSocket *sock);
-int nodeGroupPoll(struct bNodeTree *nodetree, struct bNodeTree *grouptree);
+bool nodeGroupPoll(struct bNodeTree *nodetree,
+ struct bNodeTree *grouptree,
+ const char **r_disabled_hint);
/* Init a new node type struct with default values and callbacks */
void node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
@@ -1065,7 +1085,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree,
struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree);
void ntreeShaderEndExecTree(struct bNodeTreeExec *exec);
-bool ntreeShaderExecTree(struct bNodeTree *ntree, int thread);
struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target);
void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
@@ -1188,6 +1207,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
#define CMP_NODE_TRACKPOS 271
#define CMP_NODE_INPAINT 272
#define CMP_NODE_DESPECKLE 273
+#define CMP_NODE_ANTIALIASING 274
#define CMP_NODE_GLARE 301
#define CMP_NODE_TONEMAP 302
@@ -1285,15 +1305,18 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list,
void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node);
void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node);
-void ntreeCompositCryptomatteSyncFromAdd(bNode *node);
+void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node);
void ntreeCompositCryptomatteSyncFromRemove(bNode *node);
bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node);
int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node);
-void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len);
+void ntreeCompositCryptomatteLayerPrefix(const Scene *scene,
+ const bNode *node,
+ char *r_prefix,
+ size_t prefix_len);
/* Update the runtime layer names with the cryptomatte layer names of the references
* render layer or image. */
-void ntreeCompositCryptomatteUpdateLayerNames(bNode *node);
-struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node);
+void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node);
+struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node);
/** \} */
@@ -1393,7 +1416,24 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1036
#define GEO_NODE_MESH_PRIMITIVE_CONE 1037
#define GEO_NODE_MESH_PRIMITIVE_LINE 1038
-#define GEO_NODE_MESH_PRIMITIVE_PLANE 1039
+#define GEO_NODE_MESH_PRIMITIVE_GRID 1039
+#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
+#define GEO_NODE_ATTRIBUTE_CLAMP 1041
+#define GEO_NODE_BOUNDING_BOX 1042
+#define GEO_NODE_SWITCH 1043
+#define GEO_NODE_ATTRIBUTE_TRANSFER 1044
+#define GEO_NODE_CURVE_TO_MESH 1045
+#define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046
+#define GEO_NODE_CURVE_RESAMPLE 1047
+#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048
+#define GEO_NODE_MATERIAL_ASSIGN 1049
+#define GEO_NODE_INPUT_MATERIAL 1050
+#define GEO_NODE_MATERIAL_REPLACE 1051
+#define GEO_NODE_MESH_TO_CURVE 1052
+#define GEO_NODE_DELETE_GEOMETRY 1053
+#define GEO_NODE_CURVE_LENGTH 1054
+#define GEO_NODE_SELECT_BY_MATERIAL 1055
+#define GEO_NODE_CONVEX_HULL 1056
/** \} */
diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh
index aa7e5180b03..4ec165aad8c 100644
--- a/source/blender/blenkernel/BKE_node_ui_storage.hh
+++ b/source/blender/blenkernel/BKE_node_ui_storage.hh
@@ -20,7 +20,6 @@
#include "BLI_hash.hh"
#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
#include "BLI_session_uuid.h"
#include "BLI_set.hh"
@@ -80,30 +79,37 @@ struct NodeWarning {
};
struct AvailableAttributeInfo {
+ std::string name;
AttributeDomain domain;
CustomDataType data_type;
uint64_t hash() const
{
- uint64_t domain_hash = (uint64_t)domain;
- uint64_t data_type_hash = (uint64_t)data_type;
- return (domain_hash * 33) ^ (data_type_hash * 89);
+ return blender::get_default_hash(name);
}
friend bool operator==(const AvailableAttributeInfo &a, const AvailableAttributeInfo &b)
{
- return a.domain == b.domain && a.data_type == b.data_type;
+ return a.name == b.name;
}
};
struct NodeUIStorage {
blender::Vector<NodeWarning> warnings;
- blender::MultiValueMap<std::string, AvailableAttributeInfo> attribute_hints;
+ blender::Set<AvailableAttributeInfo> attribute_hints;
};
struct NodeTreeUIStorage {
+ std::mutex mutex;
blender::Map<NodeTreeEvaluationContext, blender::Map<std::string, NodeUIStorage>> context_map;
- std::mutex context_map_mutex;
+
+ /**
+ * Attribute search uses this to store the fake info for the string typed into a node, in order
+ * to pass the info to the execute callback that sets node socket values. This is mutable since
+ * we can count on only one attribute search being open at a time, and there is no real data
+ * stored here.
+ */
+ mutable AvailableAttributeInfo dummy_info_for_search;
};
const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C,
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 12c40e891c9..f3a5c794de8 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -34,6 +34,7 @@ struct Base;
struct BoundBox;
struct Curve;
struct Depsgraph;
+struct GeometrySet;
struct GpencilModifierData;
struct HookGpencilModifierData;
struct HookModifierData;
@@ -69,6 +70,10 @@ void BKE_object_free_curve_cache(struct Object *ob);
void BKE_object_free_derived_caches(struct Object *ob);
void BKE_object_free_caches(struct Object *object);
+void BKE_object_preview_geometry_set_add(struct Object *ob,
+ const uint64_t key,
+ struct GeometrySet *geometry_set);
+
void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd);
void BKE_object_modifier_gpencil_hook_reset(struct Object *ob,
struct HookGpencilModifierData *hmd);
@@ -336,6 +341,12 @@ struct Mesh *BKE_object_get_evaluated_mesh(struct Object *object);
struct Mesh *BKE_object_get_pre_modified_mesh(struct Object *object);
struct Mesh *BKE_object_get_original_mesh(struct Object *object);
+/* Lattice accessors.
+ * These functions return either the regular lattice, or the edit-mode lattice,
+ * whichever is currently in use. */
+struct Lattice *BKE_object_get_lattice(const struct Object *object);
+struct Lattice *BKE_object_get_evaluated_lattice(const struct Object *object);
+
int BKE_object_insert_ptcache(struct Object *ob);
void BKE_object_delete_ptcache(struct Object *ob, int index);
struct KeyBlock *BKE_object_shapekey_insert(struct Main *bmain,
@@ -363,6 +374,7 @@ struct MovieClip *BKE_object_movieclip_get(struct Scene *scene,
void BKE_object_runtime_reset(struct Object *object);
void BKE_object_runtime_reset_on_copy(struct Object *object, const int flag);
+void BKE_object_runtime_free_data(struct Object *object);
void BKE_object_batch_cache_dirty_tag(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 228b52123f3..73413b61456 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -486,7 +486,11 @@ typedef struct SculptSession {
/* Total number of polys of the base mesh. */
int totfaces;
/* Face sets store its visibility in the sign of the integer, using the absolute value as the
- * Face Set ID. Positive IDs are visible, negative IDs are hidden. */
+ * Face Set ID. Positive IDs are visible, negative IDs are hidden.
+ * The 0 ID is not used by the tools or the visibility system, it is just used when creating new
+ * geometry (the trim tool, for example) to detect which geometry was just added, so it can be
+ * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set
+ * to 0. */
int *face_sets;
/* BMesh for dynamic topology sculpting */
diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh
deleted file mode 100644
index bbee09c7bf4..00000000000
--- a/source/blender/blenkernel/BKE_persistent_data_handle.hh
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup bke
- *
- * A PersistentDataHandle is a weak reference to some data in a Blender file. The handle itself is
- * just a number. A PersistentDataHandleMap is used to convert between handles and the actual data.
- */
-
-#include "BLI_map.hh"
-
-#include "DNA_ID.h"
-
-struct Collection;
-struct Object;
-
-namespace blender::bke {
-
-class PersistentDataHandleMap;
-
-class PersistentDataHandle {
- private:
- /* Negative values indicate that the handle is "empty". */
- int32_t handle_;
-
- friend PersistentDataHandleMap;
-
- protected:
- PersistentDataHandle(int handle) : handle_(handle)
- {
- }
-
- public:
- PersistentDataHandle() : handle_(-1)
- {
- }
-
- friend bool operator==(const PersistentDataHandle &a, const PersistentDataHandle &b)
- {
- return a.handle_ == b.handle_;
- }
-
- friend bool operator!=(const PersistentDataHandle &a, const PersistentDataHandle &b)
- {
- return !(a == b);
- }
-
- friend std::ostream &operator<<(std::ostream &stream, const PersistentDataHandle &a)
- {
- stream << a.handle_;
- return stream;
- }
-
- uint64_t hash() const
- {
- return static_cast<uint64_t>(handle_);
- }
-};
-
-class PersistentIDHandle : public PersistentDataHandle {
- friend PersistentDataHandleMap;
- using PersistentDataHandle::PersistentDataHandle;
-};
-
-class PersistentObjectHandle : public PersistentIDHandle {
- friend PersistentDataHandleMap;
- using PersistentIDHandle::PersistentIDHandle;
-};
-
-class PersistentCollectionHandle : public PersistentIDHandle {
- friend PersistentDataHandleMap;
- using PersistentIDHandle::PersistentIDHandle;
-};
-
-class PersistentDataHandleMap {
- private:
- Map<int32_t, ID *> id_by_handle_;
- Map<ID *, int32_t> handle_by_id_;
-
- public:
- void add(int32_t handle, ID &id)
- {
- BLI_assert(handle >= 0);
- handle_by_id_.add(&id, handle);
- id_by_handle_.add(handle, &id);
- }
-
- PersistentIDHandle lookup(ID *id) const
- {
- const int handle = handle_by_id_.lookup_default(id, -1);
- return PersistentIDHandle(handle);
- }
-
- PersistentObjectHandle lookup(Object *object) const
- {
- const int handle = handle_by_id_.lookup_default((ID *)object, -1);
- 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);
- return id;
- }
-
- Object *lookup(const PersistentObjectHandle &handle) const
- {
- ID *id = this->lookup((const PersistentIDHandle &)handle);
- if (id == nullptr) {
- return nullptr;
- }
- if (GS(id->name) != ID_OB) {
- return nullptr;
- }
- 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_scene.h b/source/blender/blenkernel/BKE_scene.h
index b2726885593..9792f819bf9 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -140,6 +140,8 @@ struct TransformOrientationSlot *BKE_scene_orientation_slot_get(struct Scene *sc
void BKE_scene_orientation_slot_set_index(struct TransformOrientationSlot *orient_slot,
int orientation);
int BKE_scene_orientation_slot_get_index(const struct TransformOrientationSlot *orient_slot);
+int BKE_scene_orientation_get_index(struct Scene *scene, int slot_index);
+int BKE_scene_orientation_get_index_from_flag(struct Scene *scene, int flag);
/* ** Scene evaluation ** */
@@ -150,6 +152,7 @@ void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bma
void BKE_scene_graph_evaluated_ensure(struct Depsgraph *depsgraph, struct Main *bmain);
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph);
+void BKE_scene_graph_update_for_newframe_ex(struct Depsgraph *depsgraph, const bool clear_recalc);
void BKE_scene_view_layer_graph_evaluated_ensure(struct Main *bmain,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h
index 4f8b21141b6..58dc90f62dc 100644
--- a/source/blender/blenkernel/BKE_softbody.h
+++ b/source/blender/blenkernel/BKE_softbody.h
@@ -47,7 +47,7 @@ typedef struct BodyPoint {
} BodyPoint;
/* allocates and initializes general main data */
-extern struct SoftBody *sbNew(struct Scene *scene);
+extern struct SoftBody *sbNew(void);
/* frees internal data and soft-body itself */
extern void sbFree(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
new file mode 100644
index 00000000000..dfbe82f31fd
--- /dev/null
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -0,0 +1,551 @@
+/*
+ * 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
+ */
+
+#include <mutex>
+
+#include "FN_generic_virtual_array.hh"
+
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_vector.hh"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+
+struct Curve;
+
+class Spline;
+using SplinePtr = std::unique_ptr<Spline>;
+
+/**
+ * A spline is an abstraction of a single branch-less curve section, its evaluation methods,
+ * and data. The spline data itself is just control points and a set of attributes by the set
+ * of "evaluated" data is often used instead.
+ *
+ * Any derived class of Spline has to manage two things:
+ * 1. Interpolating arbitrary attribute data from the control points to evaluated points.
+ * 2. Evaluating the positions based on the stored control point data.
+ *
+ * Beyond that, everything is the base class's responsibility, with minor exceptions. Further
+ * evaluation happens in a layer on top of the evaluated points generated by the derived types.
+ *
+ * There are a few methods to evaluate a spline:
+ * 1. #evaluated_positions and #interpolate_to_evaluated_points give data for the initial
+ * evaluated points, depending on the resolution.
+ * 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups
+ * along the length of a curve.
+ * 3. #sample_uniform_index_factors returns an array that stores uniform-length samples
+ * along the spline which can be used to interpolate data from method 1.
+ *
+ * Commonly used evaluated data is stored in caches on the spline itself so that operations on
+ * splines don't need to worry about taking ownership of evaluated data when they don't need to.
+ */
+class Spline {
+ public:
+ enum class Type {
+ Bezier,
+ NURBS,
+ Poly,
+ };
+
+ enum NormalCalculationMode {
+ ZUp,
+ Minimum,
+ Tangent,
+ };
+ /* Only #Zup is supported at the moment. */
+ NormalCalculationMode normal_mode;
+
+ blender::bke::CustomDataAttributes attributes;
+
+ protected:
+ Type type_;
+ bool is_cyclic_ = false;
+
+ /** Direction of the spline at each evaluated point. */
+ mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
+ mutable std::mutex tangent_cache_mutex_;
+ mutable bool tangent_cache_dirty_ = true;
+
+ /** Normal direction vectors for each evaluated point. */
+ mutable blender::Vector<blender::float3> evaluated_normals_cache_;
+ mutable std::mutex normal_cache_mutex_;
+ mutable bool normal_cache_dirty_ = true;
+
+ /** Accumulated lengths along the evaluated points. */
+ mutable blender::Vector<float> evaluated_lengths_cache_;
+ mutable std::mutex length_cache_mutex_;
+ mutable bool length_cache_dirty_ = true;
+
+ public:
+ virtual ~Spline() = default;
+ Spline(const Type type) : type_(type)
+ {
+ }
+ Spline(Spline &other) : attributes(other.attributes), type_(other.type_)
+ {
+ copy_base_settings(other, *this);
+ }
+
+ virtual SplinePtr copy() const = 0;
+ /** Return a new spline with the same type and settings like "cyclic", but without any data. */
+ virtual SplinePtr copy_settings() const = 0;
+
+ Spline::Type type() const;
+
+ /** Return the number of control points. */
+ virtual int size() const = 0;
+ int segments_size() const;
+ bool is_cyclic() const;
+ void set_cyclic(const bool value);
+
+ virtual void resize(const int size) = 0;
+ virtual blender::MutableSpan<blender::float3> positions() = 0;
+ virtual blender::Span<blender::float3> positions() const = 0;
+ virtual blender::MutableSpan<float> radii() = 0;
+ virtual blender::Span<float> radii() const = 0;
+ virtual blender::MutableSpan<float> tilts() = 0;
+ virtual blender::Span<float> tilts() const = 0;
+
+ virtual void translate(const blender::float3 &translation);
+ virtual void transform(const blender::float4x4 &matrix);
+
+ /**
+ * Mark all caches for re-computation. This must be called after any operation that would
+ * change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
+ */
+ virtual void mark_cache_invalid() = 0;
+ virtual int evaluated_points_size() const = 0;
+ int evaluated_edges_size() const;
+
+ float length() const;
+
+ virtual blender::Span<blender::float3> evaluated_positions() const = 0;
+
+ blender::Span<float> evaluated_lengths() const;
+ blender::Span<blender::float3> evaluated_tangents() const;
+ blender::Span<blender::float3> evaluated_normals() const;
+
+ void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
+
+ struct LookupResult {
+ /**
+ * The index of the evaluated point before the result location. In other words, the index of
+ * the edge that the result lies on. If the sampled factor/length is the very end of the
+ * spline, this will be the second to last index, if it's the very beginning, this will be 0.
+ */
+ int evaluated_index;
+ /**
+ * The index of the evaluated point after the result location, accounting for wrapping when
+ * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will
+ * be the last index (#evaluated_points_size - 1).
+ */
+ int next_evaluated_index;
+ /**
+ * The portion of the way from the evaluated point at #evaluated_index to the next point.
+ * If the sampled factor/length is the very end of the spline, this will be the 1.0f
+ */
+ float factor;
+ };
+ LookupResult lookup_evaluated_factor(const float factor) const;
+ LookupResult lookup_evaluated_length(const float length) const;
+
+ blender::Array<float> sample_uniform_index_factors(const int samples_size) const;
+ LookupResult lookup_data_from_index_factor(const float index_factor) const;
+
+ void sample_based_on_index_factors(const blender::fn::GVArray &src,
+ blender::Span<float> index_factors,
+ blender::fn::GMutableSpan dst) const;
+ template<typename T>
+ void sample_based_on_index_factors(const blender::VArray<T> &src,
+ blender::Span<float> index_factors,
+ blender::MutableSpan<T> dst) const
+ {
+ this->sample_based_on_index_factors(
+ blender::fn::GVArray_For_VArray(src), index_factors, blender::fn::GMutableSpan(dst));
+ }
+ template<typename T>
+ void sample_based_on_index_factors(blender::Span<T> src,
+ blender::Span<float> index_factors,
+ blender::MutableSpan<T> dst) const
+ {
+ this->sample_based_on_index_factors(blender::VArray_For_Span(src), index_factors, dst);
+ }
+
+ /**
+ * Interpolate a virtual array of data with the size of the number of control points to the
+ * evaluated points. For poly splines, the lifetime of the returned virtual array must not
+ * exceed the lifetime of the input data.
+ */
+ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const = 0;
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(blender::fn::GSpan data) const;
+ template<typename T>
+ blender::fn::GVArray_Typed<T> interpolate_to_evaluated_points(blender::Span<T> data) const
+ {
+ return blender::fn::GVArray_Typed<T>(
+ this->interpolate_to_evaluated_points(blender::fn::GSpan(data)));
+ }
+
+ protected:
+ virtual void correct_end_tangents() const = 0;
+ /** Copy settings stored in the base spline class. */
+ static void copy_base_settings(const Spline &src, Spline &dst)
+ {
+ dst.normal_mode = src.normal_mode;
+ dst.is_cyclic_ = src.is_cyclic_;
+ }
+};
+
+/**
+ * A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature
+ * by constraining the alignment of curve handles. Evaluation stores the positions and a map of
+ * factors and indices in a list of floats, which is then used to interpolate any other data.
+ */
+class BezierSpline final : public Spline {
+ public:
+ enum class HandleType {
+ /** The handle can be moved anywhere, and doesn't influence the point's other handle. */
+ Free,
+ /** The location is automatically calculated to be smooth. */
+ Auto,
+ /** The location is calculated to point to the next/previous control point. */
+ Vector,
+ /** The location is constrained to point in the opposite direction as the other handle. */
+ Align,
+ };
+
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+ int resolution_;
+
+ blender::Vector<HandleType> handle_types_left_;
+ blender::Vector<HandleType> handle_types_right_;
+
+ /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
+ mutable blender::Vector<blender::float3> handle_positions_left_;
+ mutable blender::Vector<blender::float3> handle_positions_right_;
+
+ mutable std::mutex auto_handle_mutex_;
+ mutable bool auto_handles_dirty_ = true;
+
+ /** Start index in evaluated points array for every control point. */
+ mutable blender::Vector<int> offset_cache_;
+ mutable std::mutex offset_cache_mutex_;
+ mutable bool offset_cache_dirty_ = true;
+
+ /** Cache of evaluated positions. */
+ mutable blender::Vector<blender::float3> evaluated_position_cache_;
+ mutable std::mutex position_cache_mutex_;
+ mutable bool position_cache_dirty_ = true;
+
+ /** Cache of "index factors" based calculated from the evaluated positions. */
+ mutable blender::Vector<float> evaluated_mapping_cache_;
+ mutable std::mutex mapping_cache_mutex_;
+ mutable bool mapping_cache_dirty_ = true;
+
+ public:
+ virtual SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
+ BezierSpline() : Spline(Type::Bezier)
+ {
+ }
+ BezierSpline(const BezierSpline &other)
+ : Spline((Spline &)other),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_),
+ resolution_(other.resolution_),
+ handle_types_left_(other.handle_types_left_),
+ handle_types_right_(other.handle_types_right_),
+ handle_positions_left_(other.handle_positions_left_),
+ handle_positions_right_(other.handle_positions_right_)
+ {
+ }
+
+ int size() const final;
+ int resolution() const;
+ void set_resolution(const int value);
+
+ void add_point(const blender::float3 position,
+ const HandleType handle_type_left,
+ const blender::float3 handle_position_left,
+ const HandleType handle_type_right,
+ const blender::float3 handle_position_right,
+ const float radius,
+ const float tilt);
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+ blender::Span<HandleType> handle_types_left() const;
+ blender::MutableSpan<HandleType> handle_types_left();
+ blender::Span<blender::float3> handle_positions_left() const;
+ blender::MutableSpan<blender::float3> handle_positions_left();
+ blender::Span<HandleType> handle_types_right() const;
+ blender::MutableSpan<HandleType> handle_types_right();
+ blender::Span<blender::float3> handle_positions_right() const;
+ blender::MutableSpan<blender::float3> handle_positions_right();
+
+ void translate(const blender::float3 &translation) override;
+ void transform(const blender::float4x4 &matrix) override;
+
+ bool point_is_sharp(const int index) const;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<int> control_point_offsets() const;
+ blender::Span<float> evaluated_mappings() const;
+ blender::Span<blender::float3> evaluated_positions() const final;
+ struct InterpolationData {
+ int control_point_index;
+ int next_control_point_index;
+ /**
+ * Linear interpolation weight between the two indices, from 0 to 1.
+ * Higher means closer to next control point.
+ */
+ float factor;
+ };
+ InterpolationData interpolation_data_from_index_factor(const float index_factor) const;
+
+ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const override;
+
+ private:
+ void ensure_auto_handles() const;
+ void correct_end_tangents() const final;
+ bool segment_is_vector(const int start_index) const;
+ void evaluate_bezier_segment(const int index,
+ const int next_index,
+ blender::MutableSpan<blender::float3> positions) const;
+};
+
+/**
+ * Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
+ * influenced by a vector of knots, weights for each point, and the order of the spline. Every
+ * mapping of data to evaluated points is handled the same way, but the positions are cached in
+ * the spline.
+ */
+class NURBSpline final : public Spline {
+ public:
+ enum class KnotsMode {
+ Normal,
+ EndPoint,
+ Bezier,
+ };
+
+ /** Method used to recalculate the knots vector when points are added or removed. */
+ KnotsMode knots_mode;
+
+ struct BasisCache {
+ /** The influence at each control point `i + #start_index`. */
+ blender::Vector<float> weights;
+ /**
+ * An offset for the start of #weights: the first control point index with a non-zero weight.
+ */
+ int start_index;
+ };
+
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+ blender::Vector<float> weights_;
+ int resolution_;
+ /**
+ * Defines the number of nearby control points that influence a given evaluated point. Higher
+ * orders give smoother results. The number of control points must be greater than or equal to
+ * this value.
+ */
+ uint8_t order_;
+
+ /**
+ * Determines where and how the control points affect the evaluated points. The length should
+ * always be the value returned by #knots_size(), and each value should be greater than or equal
+ * to the previous. Only invalidated when a point is added or removed.
+ */
+ mutable blender::Vector<float> knots_;
+ mutable std::mutex knots_mutex_;
+ mutable bool knots_dirty_ = true;
+
+ /** Cache of control point influences on each evaluated point. */
+ mutable blender::Vector<BasisCache> basis_cache_;
+ mutable std::mutex basis_cache_mutex_;
+ mutable bool basis_cache_dirty_ = true;
+
+ /**
+ * Cache of position data calculated from the basis cache. Though it is interpolated
+ * in the same way as any other attribute, it is stored to save unnecessary recalculation.
+ */
+ mutable blender::Vector<blender::float3> evaluated_position_cache_;
+ mutable std::mutex position_cache_mutex_;
+ mutable bool position_cache_dirty_ = true;
+
+ public:
+ SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
+ NURBSpline() : Spline(Type::NURBS)
+ {
+ }
+ NURBSpline(const NURBSpline &other)
+ : Spline((Spline &)other),
+ knots_mode(other.knots_mode),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_),
+ weights_(other.weights_),
+ resolution_(other.resolution_),
+ order_(other.order_)
+ {
+ }
+
+ int size() const final;
+ int resolution() const;
+ void set_resolution(const int value);
+ uint8_t order() const;
+ void set_order(const uint8_t value);
+
+ void add_point(const blender::float3 position,
+ const float radius,
+ const float tilt,
+ const float weight);
+
+ bool check_valid_size_and_order() const;
+ int knots_size() const;
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+ blender::Span<float> knots() const;
+
+ blender::MutableSpan<float> weights();
+ blender::Span<float> weights() const;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<blender::float3> evaluated_positions() const final;
+
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const final;
+
+ protected:
+ void correct_end_tangents() const final;
+ void calculate_knots() const;
+ void calculate_basis_cache() const;
+};
+
+/**
+ * A Poly spline is like a bezier spline with a resolution of one. The main reason to distinguish
+ * the two is for reduced complexity and increased performance, since interpolating data to control
+ * points does not change it.
+ */
+class PolySpline final : public Spline {
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+
+ public:
+ SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
+ PolySpline() : Spline(Type::Poly)
+ {
+ }
+ PolySpline(const PolySpline &other)
+ : Spline((Spline &)other),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_)
+ {
+ }
+
+ int size() const final;
+
+ void add_point(const blender::float3 position, const float radius, const float tilt);
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<blender::float3> evaluated_positions() const final;
+
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const final;
+
+ protected:
+ void correct_end_tangents() const final;
+};
+
+/**
+ * A #CurveEval corresponds to the #Curve object data. The name is different for clarity, since
+ * more of the data is stored in the splines, but also just to be different than the name in DNA.
+ */
+struct CurveEval {
+ private:
+ blender::Vector<SplinePtr> splines_;
+
+ public:
+ blender::bke::CustomDataAttributes attributes;
+
+ CurveEval() = default;
+ CurveEval(const CurveEval &other) : attributes(other.attributes)
+ {
+ for (const SplinePtr &spline : other.splines()) {
+ this->add_spline(spline->copy());
+ }
+ }
+
+ blender::Span<SplinePtr> splines() const;
+ blender::MutableSpan<SplinePtr> splines();
+
+ void add_spline(SplinePtr spline);
+ void remove_splines(blender::IndexMask mask);
+
+ void translate(const blender::float3 &translation);
+ void transform(const blender::float4x4 &matrix);
+ void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
+
+ blender::Array<int> control_point_offsets() const;
+ blender::Array<int> evaluated_point_offsets() const;
+
+ void assert_valid_point_attributes() const;
+};
+
+std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve);
diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h
index c4af9ab40e2..b28215a72d1 100644
--- a/source/blender/blenkernel/BKE_unit.h
+++ b/source/blender/blenkernel/BKE_unit.h
@@ -82,12 +82,13 @@ enum {
B_UNIT_MASS = 4,
B_UNIT_ROTATION = 5,
B_UNIT_TIME = 6,
- B_UNIT_VELOCITY = 7,
- B_UNIT_ACCELERATION = 8,
- B_UNIT_CAMERA = 9,
- B_UNIT_POWER = 10,
- B_UNIT_TEMPERATURE = 11,
- B_UNIT_TYPE_TOT = 12,
+ B_UNIT_TIME_ABSOLUTE = 7,
+ B_UNIT_VELOCITY = 8,
+ B_UNIT_ACCELERATION = 9,
+ B_UNIT_CAMERA = 10,
+ B_UNIT_POWER = 11,
+ B_UNIT_TEMPERATURE = 12,
+ B_UNIT_TYPE_TOT = 13,
};
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h
index 53626dbeb1b..cf755827a6c 100644
--- a/source/blender/blenkernel/BKE_volume.h
+++ b/source/blender/blenkernel/BKE_volume.h
@@ -78,16 +78,17 @@ extern void (*BKE_volume_batch_cache_free_cb)(struct Volume *volume);
typedef struct VolumeGrid VolumeGrid;
-bool BKE_volume_load(struct Volume *volume, struct Main *bmain);
+bool BKE_volume_load(const struct Volume *volume, const struct Main *bmain);
void BKE_volume_unload(struct Volume *volume);
bool BKE_volume_is_loaded(const struct Volume *volume);
int BKE_volume_num_grids(const struct Volume *volume);
const char *BKE_volume_grids_error_msg(const struct Volume *volume);
const char *BKE_volume_grids_frame_filepath(const struct Volume *volume);
-VolumeGrid *BKE_volume_grid_get(const struct Volume *volume, int grid_index);
-VolumeGrid *BKE_volume_grid_active_get(const struct Volume *volume);
-VolumeGrid *BKE_volume_grid_find(const struct Volume *volume, const char *name);
+const VolumeGrid *BKE_volume_grid_get_for_read(const struct Volume *volume, int grid_index);
+VolumeGrid *BKE_volume_grid_get_for_write(struct Volume *volume, int grid_index);
+const VolumeGrid *BKE_volume_grid_active_get_for_read(const struct Volume *volume);
+const VolumeGrid *BKE_volume_grid_find_for_read(const struct Volume *volume, const char *name);
/* Grid
*
@@ -109,8 +110,8 @@ typedef enum VolumeGridType {
VOLUME_GRID_POINTS,
} VolumeGridType;
-bool BKE_volume_grid_load(const struct Volume *volume, struct VolumeGrid *grid);
-void BKE_volume_grid_unload(const struct Volume *volume, struct VolumeGrid *grid);
+bool BKE_volume_grid_load(const struct Volume *volume, const struct VolumeGrid *grid);
+void BKE_volume_grid_unload(const struct Volume *volume, const struct VolumeGrid *grid);
bool BKE_volume_grid_is_loaded(const struct VolumeGrid *grid);
/* Metadata */
@@ -119,9 +120,6 @@ VolumeGridType BKE_volume_grid_type(const struct VolumeGrid *grid);
int BKE_volume_grid_channels(const struct VolumeGrid *grid);
void BKE_volume_grid_transform_matrix(const struct VolumeGrid *grid, float mat[4][4]);
-/* Bounds */
-bool BKE_volume_grid_bounds(const struct VolumeGrid *grid, float min[3], float max[3]);
-
/* Volume Editing
*
* These are intended for modifiers to use on evaluated datablocks.
@@ -145,8 +143,8 @@ int BKE_volume_simplify_level(const struct Depsgraph *depsgraph);
float BKE_volume_simplify_factor(const struct Depsgraph *depsgraph);
/* File Save */
-bool BKE_volume_save(struct Volume *volume,
- struct Main *bmain,
+bool BKE_volume_save(const struct Volume *volume,
+ const struct Main *bmain,
struct ReportList *reports,
const char *filepath);
@@ -159,13 +157,26 @@ bool BKE_volume_save(struct Volume *volume,
* Access to OpenVDB grid for C++. These will automatically load grids from
* file or copy shared grids to make them writeable. */
-#if defined(__cplusplus) && defined(WITH_OPENVDB)
-# include <openvdb/openvdb.h>
-# include <openvdb/points/PointDataGrid.h>
+#ifdef __cplusplus
+# include "BLI_float3.hh"
+# include "BLI_float4x4.hh"
+
+bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max);
+
+# ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+# include <openvdb/points/PointDataGrid.h>
+
+bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid,
+ blender::float3 &r_min,
+ blender::float3 &r_max);
+
+openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid,
+ const blender::float4x4 &transform);
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const struct VolumeGrid *grid);
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const struct Volume *volume,
- struct VolumeGrid *grid);
+ const struct VolumeGrid *grid);
openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const struct Volume *volume,
struct VolumeGrid *grid,
const bool clear);
@@ -212,4 +223,5 @@ openvdb::GridBase::Ptr BKE_volume_grid_create_with_changed_resolution(
const openvdb::GridBase &old_grid,
const float resolution_factor);
+# endif
#endif
diff --git a/source/blender/blenkernel/BKE_volume_render.h b/source/blender/blenkernel/BKE_volume_render.h
index d7553ccb10b..c959605721e 100644
--- a/source/blender/blenkernel/BKE_volume_render.h
+++ b/source/blender/blenkernel/BKE_volume_render.h
@@ -43,17 +43,17 @@ typedef struct DenseFloatVolumeGrid {
} DenseFloatVolumeGrid;
bool BKE_volume_grid_dense_floats(const struct Volume *volume,
- struct VolumeGrid *volume_grid,
+ const struct VolumeGrid *volume_grid,
DenseFloatVolumeGrid *r_dense_grid);
void BKE_volume_dense_float_grid_clear(DenseFloatVolumeGrid *dense_grid);
/* Wireframe */
typedef void (*BKE_volume_wireframe_cb)(
- void *userdata, float (*verts)[3], int (*edges)[2], int totvert, int totedge);
+ void *userdata, const float (*verts)[3], const int (*edges)[2], int totvert, int totedge);
void BKE_volume_grid_wireframe(const struct Volume *volume,
- struct VolumeGrid *volume_grid,
+ const struct VolumeGrid *volume_grid,
BKE_volume_wireframe_cb cb,
void *cb_userdata);
@@ -63,7 +63,7 @@ typedef void (*BKE_volume_selection_surface_cb)(
void *userdata, float (*verts)[3], int (*tris)[3], int totvert, int tottris);
void BKE_volume_grid_selection_surface(const struct Volume *volume,
- struct VolumeGrid *volume_grid,
+ const struct VolumeGrid *volume_grid,
BKE_volume_selection_surface_cb cb,
void *cb_userdata);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 310ac6c4903..021d7e15814 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SRC
intern/CCGSubSurf_util.c
intern/DerivedMesh.cc
intern/action.c
+ intern/action_mirror.c
intern/addon.c
intern/anim_data.c
intern/anim_path.c
@@ -111,12 +112,13 @@ set(SRC
intern/curve_convert.c
intern/curve_decimate.c
intern/curve_deform.c
+ intern/curve_eval.cc
intern/curveprofile.c
intern/customdata.c
intern/customdata_file.c
intern/data_transfer.c
intern/deform.c
- intern/displist.c
+ intern/displist.cc
intern/displist_tangent.c
intern/dynamicpaint.c
intern/editlattice.c
@@ -126,11 +128,13 @@ set(SRC
intern/editmesh_tangent.c
intern/effect.c
intern/fcurve.c
+ intern/fcurve_cache.c
intern/fcurve_driver.c
intern/fluid.c
intern/fmodifier.c
intern/font.c
intern/freestyle.c
+ intern/geometry_component_curve.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
@@ -188,6 +192,7 @@ set(SRC
intern/mesh_remap.c
intern/mesh_remesh_voxel.c
intern/mesh_runtime.c
+ intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_validate.c
intern/mesh_validate.cc
@@ -210,7 +215,7 @@ set(SRC
intern/node_ui_storage.cc
intern/object.c
intern/object_deform.c
- intern/object_dupli.c
+ intern/object_dupli.cc
intern/object_facemap.c
intern/object_update.c
intern/ocean.c
@@ -238,6 +243,10 @@ set(SRC
intern/softbody.c
intern/sound.c
intern/speaker.c
+ intern/spline_base.cc
+ intern/spline_bezier.cc
+ intern/spline_nurbs.cc
+ intern/spline_poly.cc
intern/studiolight.c
intern/subdiv.c
intern/subdiv_ccg.c
@@ -319,6 +328,7 @@ set(SRC
BKE_customdata_file.h
BKE_data_transfer.h
BKE_deform.h
+ BKE_spline.hh
BKE_displist.h
BKE_displist_tangent.h
BKE_duplilist.h
@@ -369,7 +379,7 @@ set(SRC
BKE_mball.h
BKE_mball_tessellate.h
BKE_mesh.h
- BKE_mesh_boolean_convert.h
+ BKE_mesh_boolean_convert.hh
BKE_mesh_fair.h
BKE_mesh_iterators.h
BKE_mesh_mapping.h
@@ -377,6 +387,7 @@ set(SRC
BKE_mesh_remap.h
BKE_mesh_remesh_voxel.h
BKE_mesh_runtime.h
+ BKE_mesh_sample.hh
BKE_mesh_tangent.h
BKE_mesh_types.h
BKE_mesh_wrapper.h
@@ -385,6 +396,7 @@ set(SRC
BKE_multires.h
BKE_nla.h
BKE_node.h
+ BKE_node_ui_storage.hh
BKE_object.h
BKE_object_deform.h
BKE_object_facemap.h
@@ -394,7 +406,6 @@ set(SRC
BKE_paint.h
BKE_particle.h
BKE_pbvh.h
- BKE_persistent_data_handle.hh
BKE_pointcache.h
BKE_pointcloud.h
BKE_preferences.h
@@ -434,10 +445,10 @@ set(SRC
nla_private.h
particle_private.h
tracking_private.h
- intern/attribute_access_intern.hh
intern/CCGSubSurf.h
intern/CCGSubSurf_inline.h
intern/CCGSubSurf_intern.h
+ intern/attribute_access_intern.hh
intern/data_transfer_intern.h
intern/lib_intern.h
intern/multires_inline.h
@@ -580,10 +591,6 @@ if(WITH_CODEC_FFMPEG)
${FFMPEG_LIBRARIES}
)
add_definitions(-DWITH_FFMPEG)
-
- remove_strict_c_flags_file(
- intern/writeffmpeg.c
- )
endif()
if(WITH_PYTHON)
@@ -742,7 +749,7 @@ if(WITH_GMP)
list(APPEND INC_SYS
${GMP_INCLUDE_DIRS}
)
- endif()
+endif()
# # Warnings as errors, this is too strict!
# if(MSVC)
@@ -762,6 +769,7 @@ if(WITH_GTESTS)
intern/fcurve_test.cc
intern/lattice_deform_test.cc
intern/layer_test.cc
+ intern/lib_id_test.cc
intern/tracking_test.cc
)
set(TEST_INC
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index 9f51ef5292f..d4dd7e248d5 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -712,11 +712,13 @@ static float (*get_orco_coords(Object *ob, BMEditMesh *em, int layer, int *free)
if (!em) {
ClothModifierData *clmd = (ClothModifierData *)BKE_modifiers_findby_type(
ob, eModifierType_Cloth);
- KeyBlock *kb = BKE_keyblock_from_key(BKE_key_from_object(ob),
- clmd->sim_parms->shapekey_rest);
+ if (clmd) {
+ KeyBlock *kb = BKE_keyblock_from_key(BKE_key_from_object(ob),
+ clmd->sim_parms->shapekey_rest);
- if (kb && kb->data) {
- return (float(*)[3])kb->data;
+ if (kb && kb->data) {
+ return (float(*)[3])kb->data;
+ }
}
}
@@ -1603,6 +1605,12 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
/* This geometry set contains the non-mesh data that might be generated by modifiers. */
GeometrySet geometry_set_final;
+ /* Add the initial mesh component, with a copy of the vertex group names from the object,
+ * since they need to be stored in the geometry set for evaluation. */
+ MeshComponent &initial_mesh_component =
+ geometry_set_final.get_component_for_write<MeshComponent>();
+ initial_mesh_component.copy_vertex_group_names_from_object(*ob);
+
/* 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
@@ -1853,9 +1861,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
BKE_id_free(nullptr, mesh_orco);
}
- /* Ensure normals calculation below is correct. */
- BLI_assert((mesh_input->flag & ME_AUTOSMOOTH) == (mesh_final->flag & ME_AUTOSMOOTH));
- BLI_assert(mesh_input->smoothresh == mesh_final->smoothresh);
+ /* Ensure normals calculation below is correct (normal settings have transferred properly).
+ * However, nodes modifiers might create meshes from scratch or transfer meshes from other
+ * objects with different settings, and in general it doesn't make sense to guarantee that
+ * the settings are the same as the original mesh. If necessary, this could become a modifier
+ * type flag. */
BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh);
/* Compute normals. */
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index f9c2a4e53ad..a7e36b09516 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -635,7 +635,7 @@ bPoseChannel *BKE_pose_channel_find_name(const bPose *pose, const char *name)
* \note Use with care, not on Armature poses but for temporal ones.
* \note (currently used for action constraints and in rebuild_pose).
*/
-bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
+bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name)
{
bPoseChannel *chan;
@@ -656,7 +656,9 @@ bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
BLI_strncpy(chan->name, name, sizeof(chan->name));
- chan->custom_scale = 1.0f;
+ copy_v3_fl(chan->custom_scale_xyz, 1.0f);
+ zero_v3(chan->custom_translation);
+ zero_v3(chan->custom_rotation_euler);
/* init vars to prevent math errors */
unit_qt(chan->quat);
@@ -815,7 +817,7 @@ void BKE_pose_copy_data_ex(bPose **dst,
*/
if (outPose->chanbase.first != outPose->chanbase.last) {
outPose->chanhash = NULL;
- BKE_pose_channels_hash_make(outPose);
+ BKE_pose_channels_hash_ensure(outPose);
}
outPose->iksolver = src->iksolver;
@@ -945,7 +947,7 @@ bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
* Removes the hash for quick lookup of channels, must
* be done when adding/removing channels.
*/
-void BKE_pose_channels_hash_make(bPose *pose)
+void BKE_pose_channels_hash_ensure(bPose *pose)
{
if (!pose->chanhash) {
bPoseChannel *pchan;
@@ -1191,7 +1193,7 @@ void BKE_pose_free(bPose *pose)
* and ID-Props, used when duplicating bones in editmode.
* (unlike copy_pose_channel_data which only does posing-related stuff).
*
- * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify)
+ * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_ensure)
*/
void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from)
{
@@ -1235,8 +1237,10 @@ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_f
if (pchan->custom) {
id_us_plus(&pchan->custom->id);
}
+ copy_v3_v3(pchan->custom_scale_xyz, pchan_from->custom_scale_xyz);
+ copy_v3_v3(pchan->custom_translation, pchan_from->custom_translation);
+ copy_v3_v3(pchan->custom_rotation_euler, pchan_from->custom_rotation_euler);
- pchan->custom_scale = pchan_from->custom_scale;
pchan->drawflag = pchan_from->drawflag;
}
@@ -1316,30 +1320,6 @@ void BKE_pose_tag_update_constraint_flags(bPose *pose)
pose->flag |= POSE_CONSTRAINTS_NEED_UPDATE_FLAGS;
}
-/* Clears all BONE_UNKEYED flags for every pose channel in every pose
- * This should only be called on frame changing, when it is acceptable to
- * do this. Otherwise, these flags should not get cleared as poses may get lost.
- */
-void framechange_poses_clear_unkeyed(Main *bmain)
-{
- Object *ob;
- bPose *pose;
- bPoseChannel *pchan;
-
- /* This needs to be done for each object that has a pose */
- /* TODO: proxies may/may not be correctly handled here... (this needs checking) */
- for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- /* we only need to do this on objects with a pose */
- if ((pose = ob->pose)) {
- for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
- }
- }
- }
-}
-
/* ************************** Bone Groups ************************** */
/* Adds a new bone-group (name may be NULL) */
@@ -1798,7 +1778,7 @@ void what_does_obaction(Object *ob,
* allocation and also will make lookup slower.
*/
if (pose->chanbase.first != pose->chanbase.last) {
- BKE_pose_channels_hash_make(pose);
+ BKE_pose_channels_hash_ensure(pose);
}
if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(pose);
diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c
new file mode 100644
index 00000000000..c975d2bfb9c
--- /dev/null
+++ b/source/blender/blenkernel/intern/action_mirror.c
@@ -0,0 +1,457 @@
+/*
+ * 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
+ *
+ * Mirror/Symmetry functions applying to actions.
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_string_utils.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_fcurve.h"
+
+#include "DEG_depsgraph.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Flip the Action (Armature/Pose Objects)
+ *
+ * This flips the action using the rest pose (not the evaluated pose).
+ *
+ * Details:
+ *
+ * - Key-frames are modified in-place, creating new key-frames is not yet supported.
+ * That could be useful if a user for example only has 2x rotation channels set.
+ * In practice users typically keyframe all rotation channels or none.
+ *
+ * - F-curve modifiers are disabled for evaluation,
+ * so the values written back to the keyframes don't include modifier offsets.
+ *
+ * - Sub-frame key-frames aren't supported,
+ * this could be added if needed without much trouble.
+ *
+ * - F-curves must have a #FCurve.bezt array (sampled curves aren't supported).
+ * \{ */
+
+/**
+ * This structure is created for each pose channels F-curve,
+ * an action be evaluated and stored in `fcurve_eval`,
+ * with the mirrored values written into `bezt_array`.
+ *
+ * Store F-curve evaluated values, constructed with a sorted array of rounded keyed-frames,
+ * passed to #action_flip_pchan_cache_init.
+ */
+struct FCurve_KeyCache {
+ /**
+ * When NULL, ignore this channel.
+ */
+ FCurve *fcurve;
+ /**
+ * Cached evaluated F-curve values (without modifiers).
+ */
+ float *fcurve_eval;
+ /**
+ * Cached #FCurve.bezt values, NULL when no key-frame exists on this frame.
+ *
+ * \note The case where two keyframes round to the same frame isn't supported.
+ * In this case only the first will be used.
+ */
+ BezTriple **bezt_array;
+};
+
+/**
+ * Assign `fkc` path, using a `path` lookup for a single value.
+ */
+static void action_flip_pchan_cache_fcurve_assign_value(struct FCurve_KeyCache *fkc,
+ int index,
+ const char *path,
+ struct FCurvePathCache *fcache)
+{
+ FCurve *fcu = BKE_fcurve_pathcache_find(fcache, path, index);
+ if (fcu && fcu->bezt) {
+ fkc->fcurve = fcu;
+ }
+}
+
+/**
+ * Assign #FCurve_KeyCache.fcurve path, using a `path` lookup for an array.
+ */
+static void action_flip_pchan_cache_fcurve_assign_array(struct FCurve_KeyCache *fkc,
+ int fkc_len,
+ const char *path,
+ struct FCurvePathCache *fcache)
+{
+ FCurve **fcurves = alloca(sizeof(*fcurves) * fkc_len);
+ if (BKE_fcurve_pathcache_find_array(fcache, path, fcurves, fkc_len)) {
+ for (int i = 0; i < fkc_len; i++) {
+ if (fcurves[i] && fcurves[i]->bezt) {
+ fkc[i].fcurve = fcurves[i];
+ }
+ }
+ }
+}
+
+/**
+ * Fill in pose channel cache for each frame in `keyed_frames`.
+ *
+ * \param keyed_frames: An array of keyed_frames to evaluate,
+ * note that each frame is rounded to the nearest int.
+ * \param keyed_frames_len: The length of the `keyed_frames` array.
+ */
+static void action_flip_pchan_cache_init(struct FCurve_KeyCache *fkc,
+ const float *keyed_frames,
+ int keyed_frames_len)
+{
+ BLI_assert(fkc->fcurve != NULL);
+
+ /* Cache the F-curve values for `keyed_frames`. */
+ const int fcurve_flag = fkc->fcurve->flag;
+ fkc->fcurve->flag |= FCURVE_MOD_OFF;
+ fkc->fcurve_eval = MEM_mallocN(sizeof(float) * keyed_frames_len, __func__);
+ for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
+ const float evaltime = keyed_frames[frame_index];
+ fkc->fcurve_eval[frame_index] = evaluate_fcurve_only_curve(fkc->fcurve, evaltime);
+ }
+ fkc->fcurve->flag = fcurve_flag;
+
+ /* Cache the #BezTriple for `keyed_frames`, or leave as NULL. */
+ fkc->bezt_array = MEM_mallocN(sizeof(*fkc->bezt_array) * keyed_frames_len, __func__);
+ BezTriple *bezt = fkc->fcurve->bezt;
+ BezTriple *bezt_end = fkc->fcurve->bezt + fkc->fcurve->totvert;
+
+ int frame_index = 0;
+ while (frame_index < keyed_frames_len) {
+ const float evaltime = keyed_frames[frame_index];
+ const float bezt_time = roundf(bezt->vec[1][0]);
+ if (bezt_time > evaltime) {
+ fkc->bezt_array[frame_index++] = NULL;
+ }
+ else {
+ if (bezt_time == evaltime) {
+ fkc->bezt_array[frame_index++] = bezt;
+ }
+ bezt++;
+ if (bezt == bezt_end) {
+ break;
+ }
+ }
+ }
+ /* Clear remaining unset keyed_frames (if-any). */
+ while (frame_index < keyed_frames_len) {
+ fkc->bezt_array[frame_index++] = NULL;
+ }
+}
+
+/**
+ */
+static void action_flip_pchan(Object *ob_arm,
+ const bPoseChannel *pchan,
+ struct FCurvePathCache *fcache)
+{
+ /* Begin F-Curve pose channel value extraction. */
+ /* Use a fixed buffer size as it's known this can only be at most:
+ * `pose.bones["{MAXBONENAME}"].rotation_quaternion`. */
+ char path_xform[256];
+ char pchan_name_esc[sizeof(((bActionChannel *)NULL)->name) * 2];
+ BLI_str_escape(pchan_name_esc, pchan->name, sizeof(pchan_name_esc));
+ const int path_xform_prefix_len = SNPRINTF(path_xform, "pose.bones[\"%s\"]", pchan_name_esc);
+ char *path_xform_suffix = path_xform + path_xform_prefix_len;
+ const int path_xform_suffix_len = sizeof(path_xform) - path_xform_prefix_len;
+
+ /* Lookup and assign all available #FCurve channels,
+ * unavailable channels are left NULL. */
+
+ /**
+ * Structure to store transformation F-curves corresponding to a pose bones transformation.
+ * Match struct member names from #bPoseChannel so macros avoid repetition.
+ *
+ * \note There is no need to read values unless they influence the 4x4 transform matrix,
+ * and no need to write values back unless they would be changed by a modified matrix.
+ * So `rotmode` needs to be read, but doesn't need to be written back to.
+ *
+ * Most bendy-bone settings don't need to be included either, flipping their RNA paths is enough.
+ * Although the X/Y settings could make sense to transform, in practice it would only
+ * work well if the rotation happened to swap X/Y alignment, leave this for now.
+ */
+ struct {
+ struct FCurve_KeyCache loc[3], eul[3], quat[4], rotAxis[3], rotAngle, size[3], rotmode;
+ } fkc_pchan = {{{NULL}}};
+
+#define FCURVE_ASSIGN_VALUE(id, path_test_suffix, index) \
+ BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
+ action_flip_pchan_cache_fcurve_assign_value(&fkc_pchan.id, index, path_xform, fcache)
+
+#define FCURVE_ASSIGN_ARRAY(id, path_test_suffix) \
+ BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
+ action_flip_pchan_cache_fcurve_assign_array( \
+ fkc_pchan.id, ARRAY_SIZE(fkc_pchan.id), path_xform, fcache)
+
+ FCURVE_ASSIGN_ARRAY(loc, ".location");
+ FCURVE_ASSIGN_ARRAY(eul, ".rotation_euler");
+ FCURVE_ASSIGN_ARRAY(quat, ".rotation_quaternion");
+ FCURVE_ASSIGN_ARRAY(rotAxis, ".rotation_axis_angle");
+ FCURVE_ASSIGN_VALUE(rotAngle, ".rotation_axis_angle", 3);
+ FCURVE_ASSIGN_ARRAY(size, ".scale");
+ FCURVE_ASSIGN_VALUE(rotmode, ".rotation_mode", 0);
+
+#undef FCURVE_ASSIGN_VALUE
+#undef FCURVE_ASSIGN_ARRAY
+
+ /* Array of F-curves, for convenient access. */
+#define FCURVE_CHANNEL_LEN (sizeof(fkc_pchan) / sizeof(struct FCurve_KeyCache))
+ FCurve *fcurve_array[FCURVE_CHANNEL_LEN];
+ int fcurve_array_len = 0;
+
+ for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
+ struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
+ if (fkc->fcurve != NULL) {
+ fcurve_array[fcurve_array_len++] = fkc->fcurve;
+ }
+ }
+
+ /* If this pose has no transform channels, there is nothing to do. */
+ if (fcurve_array_len == 0) {
+ return;
+ }
+
+ /* Calculate an array of frames used by any of the key-frames in `fcurve_array`. */
+ int keyed_frames_len;
+ const float *keyed_frames = BKE_fcurves_calc_keyed_frames(
+ fcurve_array, fcurve_array_len, &keyed_frames_len);
+
+ /* Initialize the pose channel curve cache from the F-curve. */
+ for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
+ struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
+ if (fkc->fcurve == NULL) {
+ continue;
+ }
+ action_flip_pchan_cache_init(fkc, keyed_frames, keyed_frames_len);
+ }
+
+ /* X-axis flipping matrix. */
+ float flip_mtx[4][4];
+ unit_m4(flip_mtx);
+ flip_mtx[0][0] = -1;
+
+ bPoseChannel *pchan_flip = NULL;
+ char pchan_name_flip[MAXBONENAME];
+ BLI_string_flip_side_name(pchan_name_flip, pchan->name, false, sizeof(pchan_name_flip));
+ if (!STREQ(pchan_name_flip, pchan->name)) {
+ pchan_flip = BKE_pose_channel_find_name(ob_arm->pose, pchan_name_flip);
+ }
+
+ float arm_mat_inv[4][4];
+ invert_m4_m4(arm_mat_inv, pchan_flip ? pchan_flip->bone->arm_mat : pchan->bone->arm_mat);
+
+ /* Now flip the transformation & write it back to the F-curves in `fkc_pchan`. */
+
+ for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
+
+ /* Temporary pose channel to write values into,
+ * using the `fkc_pchan` values, falling back to the values in the pose channel. */
+ bPoseChannel pchan_temp = *pchan;
+
+ /* Load the values into the channel. */
+#define READ_VALUE_FLT(id) \
+ if (fkc_pchan.id.fcurve_eval != NULL) { \
+ pchan_temp.id = fkc_pchan.id.fcurve_eval[frame_index]; \
+ } \
+ ((void)0)
+
+#define READ_VALUE_INT(id) \
+ if (fkc_pchan.id.fcurve_eval != NULL) { \
+ pchan_temp.id = floorf(fkc_pchan.id.fcurve_eval[frame_index] + 0.5f); \
+ } \
+ ((void)0)
+
+#define READ_ARRAY_FLT(id) \
+ for (int i = 0; i < ARRAY_SIZE(pchan_temp.id); i++) { \
+ READ_VALUE_FLT(id[i]); \
+ } \
+ ((void)0)
+
+ READ_ARRAY_FLT(loc);
+ READ_ARRAY_FLT(eul);
+ READ_ARRAY_FLT(quat);
+ READ_ARRAY_FLT(rotAxis);
+ READ_VALUE_FLT(rotAngle);
+ READ_ARRAY_FLT(size);
+ READ_VALUE_INT(rotmode);
+
+#undef READ_ARRAY_FLT
+#undef READ_VALUE_FLT
+#undef READ_VALUE_INT
+
+ float chan_mat[4][4];
+ BKE_pchan_to_mat4(&pchan_temp, chan_mat);
+
+ /* Move to the pose-space. */
+ mul_m4_m4m4(chan_mat, pchan->bone->arm_mat, chan_mat);
+
+ /* Flip the matrix. */
+ mul_m4_m4m4(chan_mat, chan_mat, flip_mtx);
+ mul_m4_m4m4(chan_mat, flip_mtx, chan_mat);
+
+ /* Move back to bone-space space, using the flipped bone if it exists. */
+ mul_m4_m4m4(chan_mat, arm_mat_inv, chan_mat);
+
+ BKE_pchan_apply_mat4(&pchan_temp, chan_mat, false);
+
+ /* Write the values back to the F-curves. */
+#define WRITE_VALUE_FLT(id) \
+ if (fkc_pchan.id.fcurve_eval != NULL) { \
+ BezTriple *bezt = fkc_pchan.id.bezt_array[frame_index]; \
+ if (bezt != NULL) { \
+ const float delta = pchan_temp.id - bezt->vec[1][1]; \
+ bezt->vec[0][1] += delta; \
+ bezt->vec[1][1] += delta; \
+ bezt->vec[2][1] += delta; \
+ } \
+ } \
+ ((void)0)
+
+#define WRITE_ARRAY_FLT(id) \
+ for (int i = 0; i < ARRAY_SIZE(pchan_temp.id); i++) { \
+ WRITE_VALUE_FLT(id[i]); \
+ } \
+ ((void)0)
+
+ /* Write the values back the the F-curves. */
+ WRITE_ARRAY_FLT(loc);
+ WRITE_ARRAY_FLT(eul);
+ WRITE_ARRAY_FLT(quat);
+ WRITE_ARRAY_FLT(rotAxis);
+ WRITE_VALUE_FLT(rotAngle);
+ WRITE_ARRAY_FLT(size);
+ /* No need to write back 'rotmode' as it can't be transformed. */
+
+#undef WRITE_ARRAY_FLT
+#undef WRITE_VALUE_FLT
+ }
+
+ /* Recalculate handles. */
+ for (int i = 0; i < fcurve_array_len; i++) {
+ calchandles_fcurve_ex(fcurve_array[i], 0);
+ }
+
+ MEM_freeN((void *)keyed_frames);
+
+ for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
+ struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
+ if (fkc->fcurve_eval) {
+ MEM_freeN(fkc->fcurve_eval);
+ }
+ if (fkc->bezt_array) {
+ MEM_freeN(fkc->bezt_array);
+ }
+ }
+}
+
+/**
+ * Swap all RNA paths left/right.
+ */
+static void action_flip_pchan_rna_paths(struct bAction *act)
+{
+ const char *path_pose_prefix = "pose.bones[\"";
+ const int path_pose_prefix_len = strlen(path_pose_prefix);
+
+ /* Tag curves that have renamed f-curves. */
+ LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
+ agrp->flag &= ~AGRP_TEMP;
+ }
+
+ LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
+ if (!STRPREFIX(fcu->rna_path, path_pose_prefix)) {
+ continue;
+ }
+
+ const char *name_esc = fcu->rna_path + path_pose_prefix_len;
+ const char *name_esc_end = BLI_str_escape_find_quote(name_esc);
+
+ /* While unlikely, an RNA path could be malformed. */
+ if (UNLIKELY(name_esc_end == NULL)) {
+ continue;
+ }
+
+ char name[MAXBONENAME];
+ const size_t name_esc_len = (size_t)(name_esc_end - name_esc);
+ const size_t name_len = BLI_str_unescape(name, name_esc, name_esc_len);
+
+ /* While unlikely, data paths could be constructed that have longer names than
+ * are currently supported. */
+ if (UNLIKELY(name_len >= sizeof(name))) {
+ continue;
+ }
+
+ /* When the flipped name differs, perform the rename. */
+ char name_flip[MAXBONENAME];
+ BLI_string_flip_side_name(name_flip, name, false, sizeof(name_flip));
+ if (!STREQ(name_flip, name)) {
+ char name_flip_esc[MAXBONENAME * 2];
+ BLI_str_escape(name_flip_esc, name_flip, sizeof(name_flip_esc));
+ char *path_flip = BLI_sprintfN("pose.bones[\"%s%s", name_flip_esc, name_esc_end);
+ MEM_freeN(fcu->rna_path);
+ fcu->rna_path = path_flip;
+
+ if (fcu->grp != NULL) {
+ fcu->grp->flag |= AGRP_TEMP;
+ }
+ }
+ }
+
+ /* Rename tagged groups. */
+ LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
+ if ((agrp->flag & AGRP_TEMP) == 0) {
+ continue;
+ }
+ agrp->flag &= ~AGRP_TEMP;
+ char name_flip[MAXBONENAME];
+ BLI_string_flip_side_name(name_flip, agrp->name, false, sizeof(name_flip));
+ if (!STREQ(name_flip, agrp->name)) {
+ STRNCPY(agrp->name, name_flip);
+ }
+ }
+}
+
+void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm)
+{
+ struct FCurvePathCache *fcache = BKE_fcurve_pathcache_create(&act->curves);
+ int i;
+ LISTBASE_FOREACH_INDEX (bPoseChannel *, pchan, &ob_arm->pose->chanbase, i) {
+ action_flip_pchan(ob_arm, pchan, fcache);
+ }
+ BKE_fcurve_pathcache_destroy(fcache);
+
+ action_flip_pchan_rna_paths(act);
+
+ DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 633d6202222..44b760aefc8 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -287,8 +287,10 @@ bool BKE_animdata_id_is_animated(const struct ID *id)
!BLI_listbase_is_empty(&adt->overrides);
}
-/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
- * `IDTypeInfo` structure). */
+/**
+ * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
+ * `IDTypeInfo` structure).
+ */
void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data)
{
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
@@ -352,7 +354,7 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
}
/* duplicate NLA data */
- BKE_nla_tracks_copy(bmain, &dadt->nla_tracks, &adt->nla_tracks, flag);
+ BKE_nla_tracks_copy_from_adt(bmain, dadt, adt, flag);
/* duplicate drivers (F-Curves) */
BKE_fcurves_copy(&dadt->drivers, &adt->drivers);
@@ -945,7 +947,7 @@ static bool nlastrips_path_rename_fix(ID *owner_id,
owner_id, prefix, oldName, newName, oldKey, newKey, &strip->act->curves, verify_paths);
}
/* Ignore own F-Curves, since those are local. */
- /* Check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
is_changed |= nlastrips_path_rename_fix(
owner_id, prefix, oldName, newName, oldKey, newKey, &strip->strips, verify_paths);
}
@@ -1175,7 +1177,7 @@ static bool nlastrips_path_remove_fix(const char *prefix, ListBase *strips)
any_removed |= fcurves_path_remove_fix(prefix, &strip->act->curves);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
any_removed |= nlastrips_path_remove_fix(prefix, &strip->strips);
}
return any_removed;
@@ -1243,7 +1245,7 @@ static void nlastrips_apply_all_curves_cb(ID *id, ListBase *strips, AllFCurvesCb
fcurves_apply_cb(id, &strip->act->curves, wrapper->func, wrapper->user_data);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
nlastrips_apply_all_curves_cb(id, &strip->strips, wrapper);
}
}
diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c
index 628e54971ce..af2b386a30a 100644
--- a/source/blender/blenkernel/intern/anim_path.c
+++ b/source/blender/blenkernel/intern/anim_path.c
@@ -42,157 +42,192 @@ static CLG_LogRef LOG = {"bke.anim"};
/* ******************************************************************** */
/* Curve Paths - for curve deforms and/or curve following */
-/**
- * Free curve path data
- *
- * \note Frees the path itself!
- * \note This is increasingly inaccurate with non-uniform #BevPoint subdivisions T24633.
- */
-void free_path(Path *path)
+static int get_bevlist_seg_array_size(const BevList *bl)
{
- if (path->data) {
- MEM_freeN(path->data);
+ if (bl->poly >= 0) {
+ /* Cyclic curve. */
+ return bl->nr;
}
- MEM_freeN(path);
+
+ return bl->nr - 1;
}
-/**
- * Calculate a curve-deform path for a curve
- * - Only called from displist.c -> #do_makeDispListCurveTypes
- */
-void calc_curvepath(Object *ob, ListBase *nurbs)
+int BKE_anim_path_get_array_size(const CurveCache *curve_cache)
+{
+ BLI_assert(curve_cache != NULL);
+
+ BevList *bl = curve_cache->bev.first;
+
+ BLI_assert(bl != NULL && bl->nr > 1);
+
+ return get_bevlist_seg_array_size(bl);
+}
+
+float BKE_anim_path_get_length(const CurveCache *curve_cache)
{
- BevList *bl;
- BevPoint *bevp, *bevpn, *bevpfirst, *bevplast;
- PathPoint *pp;
- Nurb *nu;
- Path *path;
- float *fp, *dist, *maxdist, xyz[3];
- float fac, d = 0, fac1, fac2;
- int a, tot, cycl = 0;
-
- /* in a path vertices are with equal differences: path->len = number of verts */
- /* NOW WITH BEVELCURVE!!! */
+ const int seg_size = BKE_anim_path_get_array_size(curve_cache);
+ return curve_cache->anim_path_accum_length[seg_size - 1];
+}
+void BKE_anim_path_calc_data(Object *ob)
+{
if (ob == NULL || ob->type != OB_CURVE) {
return;
}
-
- if (ob->runtime.curve_cache->path) {
- free_path(ob->runtime.curve_cache->path);
+ if (ob->runtime.curve_cache == NULL) {
+ CLOG_WARN(&LOG, "No curve cache!");
+ return;
}
- ob->runtime.curve_cache->path = NULL;
-
- /* weak! can only use first curve */
- bl = ob->runtime.curve_cache->bev.first;
+ /* We only use the first curve. */
+ BevList *bl = ob->runtime.curve_cache->bev.first;
if (bl == NULL || !bl->nr) {
+ CLOG_WARN(&LOG, "No bev list data!");
return;
}
- nu = nurbs->first;
+ /* Free old data. */
+ if (ob->runtime.curve_cache->anim_path_accum_length) {
+ MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
+ }
- ob->runtime.curve_cache->path = path = MEM_callocN(sizeof(Path), "calc_curvepath");
+ /* We assume that we have at least two points.
+ * If there is less than two points in the curve,
+ * no BevList should have been generated.
+ */
+ BLI_assert(bl->nr > 1);
- /* if POLY: last vertice != first vertice */
- cycl = (bl->poly != -1);
+ const int seg_size = get_bevlist_seg_array_size(bl);
+ float *len_data = (float *)MEM_mallocN(sizeof(float) * seg_size, "calcpathdist");
+ ob->runtime.curve_cache->anim_path_accum_length = len_data;
- tot = cycl ? bl->nr : bl->nr - 1;
+ BevPoint *bp_arr = bl->bevpoints;
+ float prev_len = 0.0f;
+ for (int i = 0; i < bl->nr - 1; i++) {
+ prev_len += len_v3v3(bp_arr[i].vec, bp_arr[i + 1].vec);
+ len_data[i] = prev_len;
+ }
- path->len = tot + 1;
- /* Exception: vector handle paths and polygon paths should be subdivided
- * at least a factor resolution. */
- if (path->len < nu->resolu * SEGMENTSU(nu)) {
- path->len = nu->resolu * SEGMENTSU(nu);
+ if (bl->poly >= 0) {
+ /* Cyclic curve. */
+ len_data[seg_size - 1] = prev_len + len_v3v3(bp_arr[0].vec, bp_arr[bl->nr - 1].vec);
}
+}
+
+static void get_curve_points_from_idx(const int idx,
+ const BevList *bl,
+ const bool is_cyclic,
+ BevPoint const **r_p0,
+ BevPoint const **r_p1,
+ BevPoint const **r_p2,
+ BevPoint const **r_p3)
+{
+ BLI_assert(idx >= 0);
+ BLI_assert(idx < bl->nr - 1 || (is_cyclic && idx < bl->nr));
+ BLI_assert(bl->nr > 1);
- dist = (float *)MEM_mallocN(sizeof(float) * (tot + 1), "calcpathdist");
+ const BevPoint *bp_arr = bl->bevpoints;
- /* all lengths in *dist */
- bevp = bevpfirst = bl->bevpoints;
- fp = dist;
- *fp = 0.0f;
- for (a = 0; a < tot; a++) {
- fp++;
- if (cycl && a == tot - 1) {
- sub_v3_v3v3(xyz, bevpfirst->vec, bevp->vec);
+ /* First segment. */
+ if (idx == 0) {
+ *r_p1 = &bp_arr[0];
+ if (is_cyclic) {
+ *r_p0 = &bp_arr[bl->nr - 1];
}
else {
- sub_v3_v3v3(xyz, (bevp + 1)->vec, bevp->vec);
+ *r_p0 = *r_p1;
}
- *fp = *(fp - 1) + len_v3(xyz);
- bevp++;
- }
+ *r_p2 = &bp_arr[1];
- path->totdist = *fp;
-
- /* the path verts in path->data */
- /* now also with TILT value */
- pp = path->data = (PathPoint *)MEM_callocN(sizeof(PathPoint) * path->len, "pathdata");
-
- bevp = bevpfirst;
- bevpn = bevp + 1;
- bevplast = bevpfirst + (bl->nr - 1);
- if (UNLIKELY(bevpn > bevplast)) {
- bevpn = cycl ? bevpfirst : bevplast;
- }
- fp = dist + 1;
- maxdist = dist + tot;
- fac = 1.0f / ((float)path->len - 1.0f);
- fac = fac * path->totdist;
-
- for (a = 0; a < path->len; a++) {
-
- d = ((float)a) * fac;
-
- /* we're looking for location (distance) 'd' in the array */
- if (LIKELY(tot > 0)) {
- while ((fp < maxdist) && (d >= *fp)) {
- fp++;
- if (bevp < bevplast) {
- bevp++;
- }
- bevpn = bevp + 1;
- if (UNLIKELY(bevpn > bevplast)) {
- bevpn = cycl ? bevpfirst : bevplast;
- }
- }
-
- fac1 = (*(fp)-d) / (*(fp) - *(fp - 1));
- fac2 = 1.0f - fac1;
+ if (bl->nr > 2) {
+ *r_p3 = &bp_arr[2];
}
else {
- fac1 = 1.0f;
- fac2 = 0.0f;
+ *r_p3 = *r_p2;
}
+ return;
+ }
- interp_v3_v3v3(pp->vec, bevp->vec, bevpn->vec, fac2);
- pp->vec[3] = fac1 * bevp->tilt + fac2 * bevpn->tilt;
- pp->radius = fac1 * bevp->radius + fac2 * bevpn->radius;
- pp->weight = fac1 * bevp->weight + fac2 * bevpn->weight;
- interp_qt_qtqt(pp->quat, bevp->quat, bevpn->quat, fac2);
- normalize_qt(pp->quat);
+ /* Last segment (or next to last in a cyclic curve). */
+ if (idx == bl->nr - 2) {
+ /* The case when the bl->nr == 2 falls in to the "first segment" check above.
+ * So here we can assume that bl->nr > 2.
+ */
+ *r_p0 = &bp_arr[idx - 1];
+ *r_p1 = &bp_arr[idx];
+ *r_p2 = &bp_arr[idx + 1];
+
+ if (is_cyclic) {
+ *r_p3 = &bp_arr[0];
+ }
+ else {
+ *r_p3 = *r_p2;
+ }
+ return;
+ }
- pp++;
+ if (idx == bl->nr - 1) {
+ /* Last segment in a cyclic curve. This should only trigger if the curve is cyclic
+ * as it gets an extra segment between the end and the start point. */
+ *r_p0 = &bp_arr[idx - 1];
+ *r_p1 = &bp_arr[idx];
+ *r_p2 = &bp_arr[0];
+ *r_p3 = &bp_arr[1];
+ return;
}
- MEM_freeN(dist);
+ /* To get here the curve has to have four curve points or more and idx can't
+ * be the first or the last segment.
+ * So we can assume that we can get four points without any special checks.
+ */
+ *r_p0 = &bp_arr[idx - 1];
+ *r_p1 = &bp_arr[idx];
+ *r_p2 = &bp_arr[idx + 1];
+ *r_p3 = &bp_arr[idx + 2];
}
-static int interval_test(const int min, const int max, int p1, const int cycl)
+static bool binary_search_anim_path(const float *accum_len_arr,
+ const int seg_size,
+ const float goal_len,
+ int *r_idx,
+ float *r_frac)
{
- if (cycl) {
- p1 = mod_i(p1 - min, (max - min + 1)) + min;
- }
- else {
- if (p1 < min) {
- p1 = min;
+ float left_len, right_len;
+ int cur_idx = 0, cur_base = 0;
+ int cur_step = seg_size - 1;
+
+ while (true) {
+ cur_idx = cur_base + cur_step / 2;
+ left_len = accum_len_arr[cur_idx];
+ right_len = accum_len_arr[cur_idx + 1];
+
+ if (left_len <= goal_len && right_len > goal_len) {
+ *r_idx = cur_idx + 1;
+ *r_frac = (goal_len - left_len) / (right_len - left_len);
+ return true;
}
- else if (p1 > max) {
- p1 = max;
+ if (cur_idx == 0) {
+ /* We ended up at the first segment. The point must be in here. */
+ *r_idx = 0;
+ *r_frac = goal_len / accum_len_arr[0];
+ return true;
}
+
+ if (UNLIKELY(cur_step == 0)) {
+ /* This should never happen unless there is something horribly wrong. */
+ CLOG_ERROR(&LOG, "Couldn't find any valid point on the animation path!");
+ BLI_assert(!"Couldn't find any valid point on the animation path!");
+ return false;
+ }
+
+ if (left_len < goal_len) {
+ /* Go to the right. */
+ cur_base = cur_idx + 1;
+ cur_step--;
+ } /* Else, go to the left. */
+
+ cur_step /= 2;
}
- return p1;
}
/**
@@ -203,66 +238,70 @@ static int interval_test(const int min, const int max, int p1, const int cycl)
*
* \return success.
*/
-bool where_on_path(const Object *ob,
- float ctime,
- float r_vec[4],
- float r_dir[3],
- float r_quat[4],
- float *r_radius,
- float *r_weight)
+bool BKE_where_on_path(const Object *ob,
+ float ctime,
+ float r_vec[4],
+ float r_dir[3],
+ float r_quat[4],
+ float *r_radius,
+ float *r_weight)
{
- Curve *cu;
- const Nurb *nu;
- const BevList *bl;
- const Path *path;
- const PathPoint *pp, *p0, *p1, *p2, *p3;
- float fac;
- float data[4];
- int cycl = 0, s0, s1, s2, s3;
- const ListBase *nurbs;
-
if (ob == NULL || ob->type != OB_CURVE) {
return false;
}
- cu = ob->data;
- if (ob->runtime.curve_cache == NULL || ob->runtime.curve_cache->path == NULL ||
- ob->runtime.curve_cache->path->data == NULL) {
- CLOG_WARN(&LOG, "no path!");
- return false;
- }
- path = ob->runtime.curve_cache->path;
- pp = path->data;
-
- /* test for cyclic */
- bl = ob->runtime.curve_cache->bev.first;
- if (!bl) {
+ Curve *cu = ob->data;
+ if (ob->runtime.curve_cache == NULL) {
+ CLOG_WARN(&LOG, "No curve cache!");
return false;
}
- if (!bl->nr) {
+ /* We only use the first curve. */
+ BevList *bl = ob->runtime.curve_cache->bev.first;
+ if (bl == NULL || !bl->nr) {
+ CLOG_WARN(&LOG, "No bev list data!");
return false;
}
- if (bl->poly > -1) {
- cycl = 1;
+
+ /* Test for cyclic curve. */
+ const bool is_cyclic = bl->poly >= 0;
+
+ if (is_cyclic) {
+ /* Wrap the time into a 0.0 - 1.0 range. */
+ if (ctime < 0.0f || ctime > 1.0f) {
+ ctime -= floorf(ctime);
+ }
}
- /* values below zero for non-cyclic curves give strange results */
- BLI_assert(cycl || ctime >= 0.0f);
+ /* The curve points for this ctime value. */
+ const BevPoint *p0, *p1, *p2, *p3;
- ctime *= (path->len - 1);
+ float frac;
+ const int seg_size = get_bevlist_seg_array_size(bl);
+ const float *accum_len_arr = ob->runtime.curve_cache->anim_path_accum_length;
+ const float goal_len = ctime * accum_len_arr[seg_size - 1];
- s1 = (int)floor(ctime);
- fac = (float)(s1 + 1) - ctime;
+ /* Are we simply trying to get the start/end point? */
+ if (ctime <= 0.0f || ctime >= 1.0f) {
+ const float clamp_time = clamp_f(ctime, 0.0f, 1.0f);
+ const int idx = clamp_time * (seg_size - 1);
+ get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
- /* path->len is corrected for cyclic */
- s0 = interval_test(0, path->len - 1 - cycl, s1 - 1, cycl);
- s1 = interval_test(0, path->len - 1 - cycl, s1, cycl);
- s2 = interval_test(0, path->len - 1 - cycl, s1 + 1, cycl);
- s3 = interval_test(0, path->len - 1 - cycl, s1 + 2, cycl);
+ if (idx == 0) {
+ frac = goal_len / accum_len_arr[0];
+ }
+ else {
+ frac = (goal_len - accum_len_arr[idx - 1]) / (accum_len_arr[idx] - accum_len_arr[idx - 1]);
+ }
+ }
+ else {
+ /* Do binary search to get the correct segment. */
+ int idx;
+ const bool found_idx = binary_search_anim_path(accum_len_arr, seg_size, goal_len, &idx, &frac);
- p0 = pp + s0;
- p1 = pp + s1;
- p2 = pp + s2;
- p3 = pp + s3;
+ if (UNLIKELY(!found_idx)) {
+ return false;
+ }
+ get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
+ }
/* NOTE: commented out for follow constraint
*
@@ -272,65 +311,68 @@ bool where_on_path(const Object *ob,
*/
// if (cu->flag & CU_FOLLOW) {
- key_curve_tangent_weights(1.0f - fac, data, KEY_BSPLINE);
+ float w[4];
+
+ key_curve_tangent_weights(frac, w, KEY_BSPLINE);
- interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, data);
+ interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
/* Make compatible with #vec_to_quat. */
negate_v3(r_dir);
//}
- nurbs = BKE_curve_editNurbs_get(cu);
+ const ListBase *nurbs = BKE_curve_editNurbs_get(cu);
if (!nurbs) {
nurbs = &cu->nurb;
}
- nu = nurbs->first;
+ const Nurb *nu = nurbs->first;
/* make sure that first and last frame are included in the vectors here */
- if (nu->type == CU_POLY) {
- key_curve_position_weights(1.0f - fac, data, KEY_LINEAR);
+ if (ELEM(nu->type, CU_POLY, CU_BEZIER, CU_NURBS)) {
+ key_curve_position_weights(frac, w, KEY_LINEAR);
}
- else if (nu->type == CU_BEZIER) {
- key_curve_position_weights(1.0f - fac, data, KEY_LINEAR);
- }
- else if (s0 == s1 || p2 == p3) {
- key_curve_position_weights(1.0f - fac, data, KEY_CARDINAL);
+ else if (p2 == p3) {
+ key_curve_position_weights(frac, w, KEY_CARDINAL);
}
else {
- key_curve_position_weights(1.0f - fac, data, KEY_BSPLINE);
+ key_curve_position_weights(frac, w, KEY_BSPLINE);
}
r_vec[0] = /* X */
- data[0] * p0->vec[0] + data[1] * p1->vec[0] + data[2] * p2->vec[0] + data[3] * p3->vec[0];
+ w[0] * p0->vec[0] + w[1] * p1->vec[0] + w[2] * p2->vec[0] + w[3] * p3->vec[0];
r_vec[1] = /* Y */
- data[0] * p0->vec[1] + data[1] * p1->vec[1] + data[2] * p2->vec[1] + data[3] * p3->vec[1];
+ w[0] * p0->vec[1] + w[1] * p1->vec[1] + w[2] * p2->vec[1] + w[3] * p3->vec[1];
r_vec[2] = /* Z */
- data[0] * p0->vec[2] + data[1] * p1->vec[2] + data[2] * p2->vec[2] + data[3] * p3->vec[2];
- r_vec[3] = /* Tilt, should not be needed since we have quat still used */
- data[0] * p0->vec[3] + data[1] * p1->vec[3] + data[2] * p2->vec[3] + data[3] * p3->vec[3];
+ w[0] * p0->vec[2] + w[1] * p1->vec[2] + w[2] * p2->vec[2] + w[3] * p3->vec[2];
+
+ /* Clamp weights to 0-1 as we don't want to extrapolate other values than position. */
+ clamp_v4(w, 0.0f, 1.0f);
+
+ /* Tilt, should not be needed since we have quat still used. */
+ r_vec[3] = w[0] * p0->tilt + w[1] * p1->tilt + w[2] * p2->tilt + w[3] * p3->tilt;
if (r_quat) {
float totfac, q1[4], q2[4];
- totfac = data[0] + data[3];
+ totfac = w[0] + w[3];
if (totfac > FLT_EPSILON) {
- interp_qt_qtqt(q1, p0->quat, p3->quat, data[3] / totfac);
+ interp_qt_qtqt(q1, p0->quat, p3->quat, w[3] / totfac);
}
else {
copy_qt_qt(q1, p1->quat);
}
- totfac = data[1] + data[2];
+ totfac = w[1] + w[2];
if (totfac > FLT_EPSILON) {
- interp_qt_qtqt(q2, p1->quat, p2->quat, data[2] / totfac);
+ interp_qt_qtqt(q2, p1->quat, p2->quat, w[2] / totfac);
}
else {
copy_qt_qt(q2, p3->quat);
}
- totfac = data[0] + data[1] + data[2] + data[3];
+ totfac = w[0] + w[1] + w[2] + w[3];
if (totfac > FLT_EPSILON) {
- interp_qt_qtqt(r_quat, q1, q2, (data[1] + data[2]) / totfac);
+ interp_qt_qtqt(r_quat, q1, q2, (w[1] + w[2]) / totfac);
}
else {
copy_qt_qt(r_quat, q2);
@@ -338,13 +380,11 @@ bool where_on_path(const Object *ob,
}
if (r_radius) {
- *r_radius = data[0] * p0->radius + data[1] * p1->radius + data[2] * p2->radius +
- data[3] * p3->radius;
+ *r_radius = w[0] * p0->radius + w[1] * p1->radius + w[2] * p2->radius + w[3] * p3->radius;
}
if (r_weight) {
- *r_weight = data[0] * p0->weight + data[1] * p1->weight + data[2] * p2->weight +
- data[3] * p3->weight;
+ *r_weight = w[0] * p0->weight + w[1] * p1->weight + w[2] * p2->weight + w[3] * p3->weight;
}
return true;
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 9a890fd02be..e347306e0ae 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1040,6 +1040,7 @@ static NlaEvalChannelSnapshot *nlaevalchan_snapshot_new(NlaEvalChannel *nec)
nec_snapshot->channel = nec;
nec_snapshot->length = length;
nlavalidmask_init(&nec_snapshot->blend_domain, length);
+ nlavalidmask_init(&nec_snapshot->remap_domain, length);
return nec_snapshot;
}
@@ -1050,6 +1051,7 @@ static void nlaevalchan_snapshot_free(NlaEvalChannelSnapshot *nec_snapshot)
BLI_assert(!nec_snapshot->is_base);
nlavalidmask_free(&nec_snapshot->blend_domain);
+ nlavalidmask_free(&nec_snapshot->remap_domain);
MEM_freeN(nec_snapshot);
}
@@ -1605,8 +1607,9 @@ static bool nla_combine_get_inverted_strip_value(const int mix_mode,
}
}
-/** Accumulate quaternion channels for Combine mode according to influence.
- * \returns blended_value = lower_values @ strip_values^infl
+/**
+ * Accumulate quaternion channels for Combine mode according to influence.
+ * \returns `blended_value = lower_values @ strip_values^infl`
*/
static void nla_combine_quaternion(const float lower_values[4],
const float strip_values[4],
@@ -1649,6 +1652,363 @@ static bool nla_combine_quaternion_get_inverted_strip_values(const float lower_v
}
/* ---------------------- */
+
+/* Assert necs and necs->channel is nonNull. */
+static void nlaevalchan_assert_nonNull(NlaEvalChannelSnapshot *necs)
+{
+ UNUSED_VARS_NDEBUG(necs);
+ BLI_assert(necs != NULL && necs->channel != NULL);
+}
+
+/* Assert that the channels given can be blended or combined together. */
+static void nlaevalchan_assert_blendOrcombine_compatible(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ NlaEvalChannelSnapshot *blended_necs)
+{
+ UNUSED_VARS_NDEBUG(lower_necs, upper_necs, blended_necs);
+ BLI_assert(!ELEM(NULL, lower_necs, blended_necs));
+ BLI_assert(upper_necs == NULL || lower_necs->length == upper_necs->length);
+ BLI_assert(lower_necs->length == blended_necs->length);
+}
+
+/* Assert that the channels given can be blended or combined together as a quaternion. */
+static void nlaevalchan_assert_blendOrcombine_compatible_quaternion(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ NlaEvalChannelSnapshot *blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, blended_necs);
+ BLI_assert(lower_necs->length == 4);
+}
+
+static void nlaevalchan_copy_values(NlaEvalChannelSnapshot *dst, NlaEvalChannelSnapshot *src)
+{
+ memcpy(dst->values, src->values, src->length * sizeof(float));
+}
+
+/**
+ * Copies lower necs to blended necs if upper necs is NULL or has zero influence.
+ * \return true if copied.
+ */
+static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ const bool has_influence = !IS_EQF(upper_influence, 0.0f);
+ if (upper_necs != NULL && has_influence) {
+ return false;
+ }
+
+ nlaevalchan_copy_values(r_blended_necs, lower_necs);
+ return true;
+}
+
+/**
+ * Based on blend-mode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ */
+static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ const int length = lower_necs->length;
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_blended_necs->values[j] = lower_necs->values[j];
+ continue;
+ }
+
+ r_blended_necs->values[j] = nla_blend_value(
+ upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
+ }
+}
+
+/**
+ * Based on mix-mode, provided by one the necs,
+ * combines lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ */
+static void nlaevalchan_combine_value(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ /* Assumes every base is the same. */
+ float *base_values = lower_necs->channel->base_snapshot.values;
+ const int length = lower_necs->length;
+ const char mix_mode = lower_necs->channel->mix_mode;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_blended_necs->values[j] = lower_necs->values[j];
+ continue;
+ }
+
+ r_blended_necs->values[j] = nla_combine_value(
+ mix_mode, base_values[j], lower_necs->values[j], upper_necs->values[j], upper_influence);
+ }
+}
+
+/**
+ * Quaternion combines lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
+ */
+static void nlaevalchan_combine_quaternion(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ /** No need to check per index. We limit to all or nothing combining for quaternions. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
+ nlaevalchan_copy_values(r_blended_necs, lower_necs);
+ return;
+ }
+
+ nla_combine_quaternion(
+ lower_necs->values, upper_necs->values, upper_influence, r_blended_necs->values);
+}
+
+/**
+ * Based on blend-mode and mix-mode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
+ *
+ * \param lower_necs: Never NULL.
+ * \param upper_necs: Can be NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param upper_necs: Never NULL.
+ *
+ */
+static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_nonNull(r_blended_necs);
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_blended_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion(lower_necs, upper_necs, upper_influence, r_blended_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value(lower_necs, upper_necs, upper_influence, r_blended_necs);
+ return;
+ }
+ default:
+ BLI_assert("Mix mode should've been handled");
+ }
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value(
+ lower_necs, upper_necs, upper_blendmode, upper_influence, r_blended_necs);
+ return;
+ }
+ default:
+ BLI_assert("Blend mode should've been handled");
+ }
+}
+
+/**
+ * Based on blend-mode, solve for the upper values such that when lower blended with upper then we
+ * get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ */
+static void nlaevalchan_blend_value_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, r_upper_necs, blended_necs);
+
+ const int length = lower_necs->length;
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_upper_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_blend_get_inverted_strip_value(upper_blendmode,
+ lower_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_upper_necs->values[j]);
+ BLI_BITMAP_SET(r_upper_necs->remap_domain.ptr, j, success);
+ }
+}
+
+/**
+ * Based on mix-mode, solve for the upper values such that when lower combined with upper then we
+ * get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ */
+static void nlaevalchan_combine_value_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, r_upper_necs, blended_necs);
+
+ /* Assumes every channel's base is the same. */
+ float *base_values = lower_necs->channel->base_snapshot.values;
+ const int length = lower_necs->length;
+ const char mix_mode = lower_necs->channel->mix_mode;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_upper_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_combine_get_inverted_strip_value(mix_mode,
+ base_values[j],
+ lower_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_upper_necs->values[j]);
+
+ BLI_BITMAP_SET(r_upper_necs->remap_domain.ptr, j, success);
+ }
+}
+
+/**
+ * Solve for the upper values such that when lower quaternion combined with upper then we get
+ * blended values as a result.
+ *
+ * All blended values must be in the remap domain. If successfully remapped, then all upper values
+ * are placed in the remap domain so caller knows the result is usable.
+ */
+static void nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, r_upper_necs, blended_necs);
+
+ /* Must check each domain index individually in case animator had a non-combine NLA strip with a
+ * subset of quaternion channels and remapping through any of them failed and thus potentially
+ * has undefined values. */
+ for (int j = 0; j < 4; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, 4);
+ return;
+ }
+ }
+
+ const bool success = nla_combine_quaternion_get_inverted_strip_values(
+ lower_necs->values, blended_necs->values, upper_influence, r_upper_necs->values);
+
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, success, 4);
+}
+
+/**
+ * Based on blend-mode and mix mode, solve for the upper values such that when lower blended or
+ * combined with upper then we get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ *
+ * \param lower_necs: Never NULL.
+ * \param blended_necs: Never NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param r_upper_necs: Never NULL.
+ */
+static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+
+ if (IS_EQF(upper_influence, 0.0f)) {
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, r_upper_necs->length);
+ return;
+ }
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_upper_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_influence, r_upper_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_influence, r_upper_necs);
+ return;
+ }
+ default:
+ BLI_assert("Mix mode should've been handled");
+ }
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_blendmode, upper_influence, r_upper_necs);
+ return;
+ }
+ default:
+ BLI_assert("Blend mode should've been handled");
+ }
+}
+
+/* ---------------------- */
/* F-Modifier stack joining/separation utilities -
* should we generalize these for BLI_listbase.h interface? */
@@ -2047,12 +2407,12 @@ static void nla_eval_domain_strips(PointerRNA *ptr,
GSet *touched_actions)
{
LISTBASE_FOREACH (NlaStrip *, strip, strips) {
- /* check strip's action */
+ /* Check strip's action. */
if (strip->act) {
nla_eval_domain_action(ptr, channels, strip->act, touched_actions);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
nla_eval_domain_strips(ptr, channels, &strip->strips, touched_actions);
}
}
@@ -2094,8 +2454,10 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels,
/* ---------------------- */
-/** Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored
- * and includes a workaround for when user is not editing in place. */
+/**
+ * Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored
+ * and includes a workaround for when user is not editing in place.
+ */
static void animsys_create_tweak_strip(const AnimData *adt,
const bool keyframing_to_strip,
NlaStrip *r_tweak_strip)
@@ -2210,8 +2572,10 @@ static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt)
return true;
}
-/** Check for special case of non-pushed action being evaluated with no NLA influence (off and no
- * strips evaluated) nor NLA interference (ensure NLA not soloing). */
+/**
+ * Check for special case of non-pushed action being evaluated with no NLA influence (off and no
+ * strips evaluated) nor NLA interference (ensure NLA not soloing).
+ */
static bool is_action_track_evaluated_without_nla(const AnimData *adt,
const bool any_strip_evaluated)
{
@@ -2491,12 +2855,13 @@ void nlasnapshot_ensure_channels(NlaEvalData *eval_data, NlaEvalSnapshot *snapsh
}
}
-/** Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
+/**
+ * Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
* to the given \a upper_blendmode and \a upper_influence.
*
- * For \a upper_snapshot, blending limited to values in the \a blend_domain. For Replace blendmode,
- * this allows the upper snapshot to have a location XYZ channel where only a subset of values are
- * blended.
+ * For \a upper_snapshot, blending limited to values in the \a blend_domain.
+ * For Replace blend-mode, this allows the upper snapshot to have a location XYZ channel
+ * where only a subset of values are blended.
*/
void nlasnapshot_blend(NlaEvalData *eval_data,
NlaEvalSnapshot *lower_snapshot,
@@ -2507,11 +2872,7 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
{
nlaeval_snapshot_ensure_size(r_blended_snapshot, eval_data->num_channels);
- const bool zero_upper_influence = IS_EQF(upper_influence, 0.0f);
-
LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
- const int length = nec->base_snapshot.length;
-
NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index);
NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
if (upper_necs == NULL && lower_necs == NULL) {
@@ -2524,49 +2885,44 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
}
NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_blended_snapshot, nec);
+ nlaevalchan_blendOrcombine(
+ lower_necs, upper_necs, upper_blendmode, upper_influence, result_necs);
+ }
+}
+
+/**
+ * Using \a blended_snapshot and \a lower_snapshot, we can solve for the \a r_upper_snapshot.
+ *
+ * Only channels that exist within \a blended_snapshot are inverted.
+ *
+ * For \a r_upper_snapshot, disables \a NlaEvalChannelSnapshot->remap_domain for failed inversions.
+ * Only values within the \a remap_domain are processed.
+ */
+void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *lower_snapshot,
+ NlaEvalSnapshot *blended_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_upper_snapshot)
+{
+ nlaeval_snapshot_ensure_size(r_upper_snapshot, eval_data->num_channels);
- /** Always copy \a lower_snapshot to result, irrelevant of whether \a upper_snapshot has a
- * corresponding channel. This only matters when \a lower_snapshot not the same as
- * \a r_blended_snapshot. */
- memcpy(result_necs->values, lower_necs->values, length * sizeof(float));
- if (upper_necs == NULL || zero_upper_influence) {
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_get(blended_snapshot, nec->index);
+ if (blended_necs == NULL) {
+ /** We assume the caller only wants a subset of channels to be inverted, those that exist
+ * within \a blended_snapshot. */
continue;
}
- if (upper_blendmode == NLASTRIP_MODE_COMBINE) {
- const int mix_mode = nec->mix_mode;
- if (mix_mode == NEC_MIX_QUATERNION) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
- continue;
- }
-
- nla_combine_quaternion(
- lower_necs->values, upper_necs->values, upper_influence, result_necs->values);
- }
- else {
- for (int j = 0; j < length; j++) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
- continue;
- }
-
- result_necs->values[j] = nla_combine_value(mix_mode,
- nec->base_snapshot.values[j],
- lower_necs->values[j],
- upper_necs->values[j],
- upper_influence);
- }
- }
+ NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
+ if (lower_necs == NULL) {
+ lower_necs = nlaeval_snapshot_find_channel(lower_snapshot->base, nec);
}
- else {
- for (int j = 0; j < length; j++) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
- continue;
- }
- result_necs->values[j] = nla_blend_value(
- upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
- }
- }
+ NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_upper_snapshot, nec);
+ nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_blendmode, upper_influence, result_necs);
}
}
@@ -2664,74 +3020,64 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
return false;
}
- /* Find the evaluation channel for the NLA stack below current strip. */
+ /** Create \a blended_snapshot and fill with input \a values. */
+ NlaEvalData *eval_data = &context->lower_eval_data;
+ NlaEvalSnapshot blended_snapshot;
+ nlaeval_snapshot_init(&blended_snapshot, eval_data, NULL);
+
NlaEvalChannelKey key = {
.ptr = *prop_ptr,
.prop = prop,
};
- /**
- * Remove lower NLA stack effects.
- *
- * Using the tweak strip's blended result and the lower snapshot value, we can solve for the
- * tweak strip value it must evaluate to.
- */
- NlaEvalData *const lower_eval_data = &context->lower_eval_data;
- NlaEvalChannel *const lower_nec = nlaevalchan_verify_key(lower_eval_data, NULL, &key);
- if ((lower_nec->base_snapshot.length != count)) {
+ NlaEvalChannel *nec = nlaevalchan_verify_key(eval_data, NULL, &key);
+ BLI_assert(nec);
+ if (nec->base_snapshot.length != count) {
BLI_assert(!"invalid value count");
+ nlaeval_snapshot_free_data(&blended_snapshot);
return false;
}
- /* Invert the blending operation to compute the desired strip values. */
- NlaEvalChannelSnapshot *const lower_nec_snapshot = nlaeval_snapshot_find_channel(
- &lower_eval_data->eval_snapshot, lower_nec);
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_ensure_channel(&blended_snapshot, nec);
+ memcpy(blended_necs->values, values, sizeof(float) * count);
+ BLI_bitmap_set_all(blended_necs->remap_domain.ptr, true, count);
- float *lower_values = lower_nec_snapshot->values;
+ /** Remove lower NLA stack effects. */
+ nlasnapshot_blend_get_inverted_upper_snapshot(eval_data,
+ &context->lower_eval_data.eval_snapshot,
+ &blended_snapshot,
+ blend_mode,
+ influence,
+ &blended_snapshot);
- if (blend_mode == NLASTRIP_MODE_COMBINE) {
- /* Quaternion combine handles all sub-channels as a unit. */
- if (lower_nec->mix_mode == NEC_MIX_QUATERNION) {
- if (r_force_all == NULL) {
- return false;
- }
+ /** Write results into \a values. */
+ bool successful_remap = true;
+ if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION &&
+ blend_mode == NLASTRIP_MODE_COMBINE) {
+ if (r_force_all != NULL) {
*r_force_all = true;
-
- if (!nla_combine_quaternion_get_inverted_strip_values(
- lower_values, values, influence, values)) {
- return false;
- }
+ index = -1;
}
else {
- float *base_values = lower_nec->base_snapshot.values;
-
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_combine_get_inverted_strip_value(lower_nec->mix_mode,
- base_values[i],
- lower_values[i],
- values[i],
- influence,
- &values[i])) {
- return false;
- }
- }
- }
+ successful_remap = false;
}
}
- else {
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_blend_get_inverted_strip_value(
- blend_mode, lower_values[i], values[i], influence, &values[i])) {
- return false;
- }
- }
+
+ for (int i = 0; i < count; i++) {
+ if (!ELEM(index, i, -1)) {
+ continue;
+ }
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, i)) {
+ successful_remap = false;
}
+
+ values[i] = blended_necs->values[i];
}
- return true;
+ nlaeval_snapshot_free_data(&blended_snapshot);
+
+ return successful_remap;
}
/**
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index 1075a46e72b..bcfd34ab42f 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -137,7 +137,7 @@ static char *blender_version_decimal(const int version)
{
static char version_str[5];
BLI_assert(version < 1000);
- BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100);
+ BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100);
return version_str;
}
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index a1ebec1d756..4ea71922df5 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -2444,7 +2444,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
static int rebuild_pose_bone(
bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p)
{
- bPoseChannel *pchan = BKE_pose_channel_verify(pose, bone->name); /* verify checks and/or adds */
+ bPoseChannel *pchan = BKE_pose_channel_ensure(pose, bone->name); /* verify checks and/or adds */
pchan->bone = bone;
pchan->parent = parchan;
@@ -2515,6 +2515,17 @@ void BKE_pchan_rebuild_bbone_handles(bPose *pose, bPoseChannel *pchan)
pchan->bbone_next = pose_channel_find_bone(pose, pchan->bone->bbone_next);
}
+void BKE_pose_channels_clear_with_null_bone(bPose *pose, const bool do_id_user)
+{
+ LISTBASE_FOREACH_MUTABLE (bPoseChannel *, pchan, &pose->chanbase) {
+ if (pchan->bone == NULL) {
+ BKE_pose_channel_free_ex(pchan, do_id_user);
+ BKE_pose_channels_hash_free(pose);
+ BLI_freelinkN(&pose->chanbase, pchan);
+ }
+ }
+}
+
/**
* Only after leave editmode, duplicating, validating older files, library syncing.
*
@@ -2526,7 +2537,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
{
Bone *bone;
bPose *pose;
- bPoseChannel *pchan, *next;
+ bPoseChannel *pchan;
int counter = 0;
/* only done here */
@@ -2549,16 +2560,9 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
}
/* and a check for garbage */
- for (pchan = pose->chanbase.first; pchan; pchan = next) {
- next = pchan->next;
- if (pchan->bone == NULL) {
- BKE_pose_channel_free_ex(pchan, do_id_user);
- BKE_pose_channels_hash_free(pose);
- BLI_freelinkN(&pose->chanbase, pchan);
- }
- }
+ BKE_pose_channels_clear_with_null_bone(pose, do_id_user);
- BKE_pose_channels_hash_make(pose);
+ BKE_pose_channels_hash_ensure(pose);
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
/* Find the custom B-Bone handles. */
@@ -2877,7 +2881,8 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden
NULL;
if (bb_custom) {
float mat[4][4], smat[4][4];
- scale_m4_fl(smat, PCHAN_CUSTOM_DRAW_SIZE(pchan));
+ scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan));
+ rescale_m4(smat, pchan->custom_scale_xyz);
mul_m4_series(mat, ob->obmat, pchan_tx->pose_mat, smat);
BKE_boundbox_minmax(bb_custom, mat, r_min, r_max);
}
diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c
index 8711a001e32..bca5503c8d2 100644
--- a/source/blender/blenkernel/intern/armature_deform.c
+++ b/source/blender/blenkernel/intern/armature_deform.c
@@ -444,7 +444,9 @@ static void armature_vert_task(void *__restrict userdata,
armature_vert_task_with_dvert(data, i, dvert);
}
-static void armature_vert_task_editmesh(void *__restrict userdata, MempoolIterData *iter)
+static void armature_vert_task_editmesh(void *__restrict userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
const ArmatureUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
@@ -452,7 +454,9 @@ static void armature_vert_task_editmesh(void *__restrict userdata, MempoolIterDa
armature_vert_task_with_dvert(data, BM_elem_index_get(v), dvert);
}
-static void armature_vert_task_editmesh_no_dvert(void *__restrict userdata, MempoolIterData *iter)
+static void armature_vert_task_editmesh_no_dvert(void *__restrict userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
const ArmatureUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
@@ -593,12 +597,16 @@ static void armature_deform_coords_impl(const Object *ob_arm,
* have already been properly set. */
BM_mesh_elem_index_ensure(em_target->bm, BM_VERT);
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+
if (use_dverts) {
- BLI_task_parallel_mempool(em_target->bm->vpool, &data, armature_vert_task_editmesh, true);
+ BLI_task_parallel_mempool(
+ em_target->bm->vpool, &data, armature_vert_task_editmesh, &settings);
}
else {
BLI_task_parallel_mempool(
- em_target->bm->vpool, &data, armature_vert_task_editmesh_no_dvert, true);
+ em_target->bm->vpool, &data, armature_vert_task_editmesh_no_dvert, &settings);
}
}
else {
diff --git a/source/blender/blenkernel/intern/armature_pose.cc b/source/blender/blenkernel/intern/armature_pose.cc
index bb371b16c42..ca11692372b 100644
--- a/source/blender/blenkernel/intern/armature_pose.cc
+++ b/source/blender/blenkernel/intern/armature_pose.cc
@@ -39,7 +39,7 @@ namespace {
using BoneNameSet = blender::Set<std::string>;
// Forward declarations.
-BoneNameSet pose_apply_find_selected_bones(const bPose *pose);
+BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose);
void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
const BoneNameSet &selected_bone_names);
void pose_apply_restore_fcurves(bAction *action);
@@ -54,7 +54,8 @@ void BKE_pose_apply_action(struct Object *ob,
return;
}
- const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(pose);
+ const bArmature *armature = (bArmature *)ob->data;
+ const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(armature, pose);
const bool limit_to_selected_bones = !selected_bone_names.is_empty();
if (limit_to_selected_bones) {
@@ -74,15 +75,14 @@ void BKE_pose_apply_action(struct Object *ob,
}
namespace {
-BoneNameSet pose_apply_find_selected_bones(const bPose *pose)
+BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose)
{
BoneNameSet selected_bone_names;
bool all_bones_selected = true;
bool no_bones_selected = true;
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
- const bool is_selected = (pchan->bone->flag & BONE_SELECTED) != 0 &&
- (pchan->bone->flag & BONE_HIDDEN_P) == 0;
+ const bool is_selected = PBONE_SELECTED(armature, pchan->bone);
all_bones_selected &= is_selected;
no_bones_selected &= !is_selected;
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 8f74b8ff054..4504f10967c 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -63,40 +63,40 @@ typedef struct tSplineIK_Tree {
bPoseChannel *root; /* bone that is the root node of the chain */
- bConstraint *con; /* constraint for this chain */
- bSplineIKConstraint *ikData; /* constraint settings for this chain */
+ bConstraint *con; /* constraint for this chain */
+ bSplineIKConstraint *ik_data; /* constraint settings for this chain */
} tSplineIK_Tree;
/* ----------- */
-/* Tag the bones in the chain formed by the given bone for IK */
+/* Tag the bones in the chain formed by the given bone for IK. */
static void splineik_init_tree_from_pchan(Scene *UNUSED(scene),
Object *UNUSED(ob),
bPoseChannel *pchan_tip)
{
- bPoseChannel *pchan, *pchanRoot = NULL;
- bPoseChannel *pchanChain[255];
+ bPoseChannel *pchan, *pchan_root = NULL;
+ bPoseChannel *pchan_chain[255];
bConstraint *con = NULL;
- bSplineIKConstraint *ikData = NULL;
- float boneLengths[255];
- float totLength = 0.0f;
+ bSplineIKConstraint *ik_data = NULL;
+ float bone_lengths[255];
+ float totlength = 0.0f;
int segcount = 0;
- /* find the SplineIK constraint */
+ /* Find the SplineIK constraint. */
for (con = pchan_tip->constraints.first; con; con = con->next) {
if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
- ikData = con->data;
+ ik_data = con->data;
- /* target can only be curve */
- if ((ikData->tar == NULL) || (ikData->tar->type != OB_CURVE)) {
+ /* Target can only be a curve. */
+ if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVE)) {
continue;
}
- /* skip if disabled */
+ /* Skip if disabled. */
if ((con->enforce == 0.0f) || (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF))) {
continue;
}
- /* otherwise, constraint is ok... */
+ /* Otherwise, constraint is ok... */
break;
}
}
@@ -104,102 +104,102 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene),
return;
}
- /* find the root bone and the chain of bones from the root to the tip
+ /* Find the root bone and the chain of bones from the root to the tip.
* NOTE: this assumes that the bones are connected, but that may not be true... */
- for (pchan = pchan_tip; pchan && (segcount < ikData->chainlen);
+ for (pchan = pchan_tip; pchan && (segcount < ik_data->chainlen);
pchan = pchan->parent, segcount++) {
- /* store this segment in the chain */
- pchanChain[segcount] = pchan;
+ /* Store this segment in the chain. */
+ pchan_chain[segcount] = pchan;
- /* if performing rebinding, calculate the length of the bone */
- boneLengths[segcount] = pchan->bone->length;
- totLength += boneLengths[segcount];
+ /* If performing rebinding, calculate the length of the bone. */
+ bone_lengths[segcount] = pchan->bone->length;
+ totlength += bone_lengths[segcount];
}
if (segcount == 0) {
return;
}
- pchanRoot = pchanChain[segcount - 1];
+ pchan_root = pchan_chain[segcount - 1];
- /* perform binding step if required */
- if ((ikData->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
+ /* Perform binding step if required. */
+ if ((ik_data->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
float segmentLen = (1.0f / (float)segcount);
- /* setup new empty array for the points list */
- if (ikData->points) {
- MEM_freeN(ikData->points);
+ /* Setup new empty array for the points list. */
+ if (ik_data->points) {
+ MEM_freeN(ik_data->points);
}
- ikData->numpoints = ikData->chainlen + 1;
- ikData->points = MEM_mallocN(sizeof(float) * ikData->numpoints, "Spline IK Binding");
+ ik_data->numpoints = ik_data->chainlen + 1;
+ ik_data->points = MEM_mallocN(sizeof(float) * ik_data->numpoints, "Spline IK Binding");
- /* bind 'tip' of chain (i.e. first joint = tip of bone with the Spline IK Constraint) */
- ikData->points[0] = 1.0f;
+ /* Bind 'tip' of chain (i.e. first joint = tip of bone with the Spline IK Constraint). */
+ ik_data->points[0] = 1.0f;
- /* perform binding of the joints to parametric positions along the curve based
- * proportion of the total length that each bone occupies
+ /* Perform binding of the joints to parametric positions along the curve based
+ * proportion of the total length that each bone occupies.
*/
for (int i = 0; i < segcount; i++) {
- /* 'head' joints, traveling towards the root of the chain
- * - 2 methods; the one chosen depends on whether we've got usable lengths
+ /* 'head' joints, traveling towards the root of the chain.
+ * - 2 methods; the one chosen depends on whether we've got usable lengths.
*/
- if ((ikData->flag & CONSTRAINT_SPLINEIK_EVENSPLITS) || (totLength == 0.0f)) {
- /* 1) equi-spaced joints */
- ikData->points[i + 1] = ikData->points[i] - segmentLen;
+ if ((ik_data->flag & CONSTRAINT_SPLINEIK_EVENSPLITS) || (totlength == 0.0f)) {
+ /* 1) Equi-spaced joints. */
+ ik_data->points[i + 1] = ik_data->points[i] - segmentLen;
}
else {
- /* 2) to find this point on the curve, we take a step from the previous joint
- * a distance given by the proportion that this bone takes
+ /* 2) To find this point on the curve, we take a step from the previous joint
+ * a distance given by the proportion that this bone takes.
*/
- ikData->points[i + 1] = ikData->points[i] - (boneLengths[i] / totLength);
+ ik_data->points[i + 1] = ik_data->points[i] - (bone_lengths[i] / totlength);
}
}
- /* spline has now been bound */
- ikData->flag |= CONSTRAINT_SPLINEIK_BOUND;
+ /* Spline has now been bound. */
+ ik_data->flag |= CONSTRAINT_SPLINEIK_BOUND;
}
- /* disallow negative values (happens with float precision) */
- CLAMP_MIN(ikData->points[segcount], 0.0f);
+ /* Disallow negative values (happens with float precision). */
+ CLAMP_MIN(ik_data->points[segcount], 0.0f);
- /* make a new Spline-IK chain, and store it in the IK chains */
+ /* Make a new Spline-IK chain, and store it in the IK chains. */
/* TODO: we should check if there is already an IK chain on this,
* since that would take precedence... */
{
- /* make new tree */
+ /* Make a new tree. */
tSplineIK_Tree *tree = MEM_callocN(sizeof(tSplineIK_Tree), "SplineIK Tree");
tree->type = CONSTRAINT_TYPE_SPLINEIK;
tree->chainlen = segcount;
- tree->totlength = totLength;
+ tree->totlength = totlength;
- /* copy over the array of links to bones in the chain (from tip to root) */
+ /* Copy over the array of links to bones in the chain (from tip to root). */
tree->chain = MEM_mallocN(sizeof(bPoseChannel *) * segcount, "SplineIK Chain");
- memcpy(tree->chain, pchanChain, sizeof(bPoseChannel *) * segcount);
+ memcpy(tree->chain, pchan_chain, sizeof(bPoseChannel *) * segcount);
- /* store reference to joint position array */
- tree->points = ikData->points;
+ /* Store reference to joint position array. */
+ tree->points = ik_data->points;
- /* store references to different parts of the chain */
- tree->root = pchanRoot;
+ /* Store references to different parts of the chain. */
+ tree->root = pchan_root;
tree->con = con;
- tree->ikData = ikData;
+ tree->ik_data = ik_data;
- /* AND! link the tree to the root */
- BLI_addtail(&pchanRoot->siktree, tree);
+ /* AND! Link the tree to the root. */
+ BLI_addtail(&pchan_root->siktree, tree);
}
- /* mark root channel having an IK tree */
- pchanRoot->flag |= POSE_IKSPLINE;
+ /* Mark root channel having an IK tree. */
+ pchan_root->flag |= POSE_IKSPLINE;
}
-/* Tag which bones are members of Spline IK chains */
+/* Tag which bones are members of Spline IK chains. */
static void splineik_init_tree(Scene *scene, Object *ob, float UNUSED(ctime))
{
bPoseChannel *pchan;
- /* find the tips of Spline IK chains,
- * which are simply the bones which have been tagged as such */
+ /* Find the tips of Spline IK chains,
+ * which are simply the bones which have been tagged as such. */
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
if (pchan->constflag & PCHAN_HAS_SPLINEIK) {
splineik_init_tree_from_pchan(scene, ob, pchan);
@@ -213,23 +213,26 @@ typedef struct tSplineIk_EvalState {
float curve_position; /* Current position along the curve. */
float curve_scale; /* Global scale to apply to curve positions. */
float locrot_offset[4][4]; /* Bone rotation and location offset inherited from parent. */
+ float prev_tail_loc[3]; /* Tail location of the previous bone. */
+ float prev_tail_radius; /* Tail curve radius of the previous bone. */
+ int prev_tail_seg_idx; /* Curve segment the previous tail bone belongs to. */
} tSplineIk_EvalState;
/* Prepare data to evaluate spline IK. */
static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *state)
{
- bSplineIKConstraint *ikData = tree->ikData;
+ bSplineIKConstraint *ik_data = tree->ik_data;
/* Make sure that the constraint targets are ok, to avoid crashes
* in case of a depsgraph bug or dependency cycle.
*/
- if (ikData->tar == NULL) {
+ if (ik_data->tar == NULL) {
return false;
}
- CurveCache *cache = ikData->tar->runtime.curve_cache;
+ CurveCache *cache = ik_data->tar->runtime.curve_cache;
- if (ELEM(NULL, cache, cache->path, cache->path->data)) {
+ if (ELEM(NULL, cache, cache->anim_path_accum_length)) {
return false;
}
@@ -237,97 +240,257 @@ static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *st
state->curve_position = 0.0f;
state->curve_scale = 1.0f;
unit_m4(state->locrot_offset);
+ zero_v3(state->prev_tail_loc);
+ state->prev_tail_radius = 1.0f;
+ state->prev_tail_seg_idx = 0;
/* Apply corrections for sensitivity to scaling. */
- if ((ikData->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) && (tree->totlength != 0.0f)) {
- /* get the current length of the curve */
- /* NOTE: this is assumed to be correct even after the curve was resized */
- float splineLen = cache->path->totdist;
+ if ((ik_data->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) && (tree->totlength != 0.0f)) {
+ /* Get the current length of the curve. */
+ /* NOTE: This is assumed to be correct even after the curve was resized. */
+ const float spline_len = BKE_anim_path_get_length(cache);
- /* calculate the scale factor to multiply all the path values by so that the
- * bone chain retains its current length, such that
+ /* Calculate the scale factor to multiply all the path values by so that the
+ * bone chain retains its current length, such that:
* maxScale * splineLen = totLength
*/
- state->curve_scale = tree->totlength / splineLen;
+ state->curve_scale = tree->totlength / spline_len;
}
return true;
}
+static void apply_curve_transform(
+ bSplineIKConstraint *ik_data, Object *ob, float radius, float r_vec[3], float *r_radius)
+{
+ /* Apply the curve's object-mode transforms to the position
+ * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root).
+ */
+ if ((ik_data->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
+ mul_m4_v3(ik_data->tar->obmat, r_vec);
+ }
+
+ /* Convert the position to pose-space. */
+ mul_m4_v3(ob->imat, r_vec);
+
+ /* Set the new radius (it should be the average value). */
+ *r_radius = (radius + *r_radius) / 2;
+}
+
+/* This function positions the tail of the bone so that it preserves the length of it.
+ * The length of the bone can be seen as a sphere radius.
+ */
+static int position_tail_on_spline(bSplineIKConstraint *ik_data,
+ const float head_pos[3],
+ const float sphere_radius,
+ const int prev_seg_idx,
+ float r_tail_pos[3],
+ float *r_new_curve_pos,
+ float *r_radius)
+{
+ /* This is using the tessellated curve data.
+ * So we are working with piece-wise linear curve segments.
+ * The same method is use in #BKE_where_on_path to get curve location data. */
+ const CurveCache *cache = ik_data->tar->runtime.curve_cache;
+ const BevList *bl = cache->bev.first;
+ BevPoint *bp = bl->bevpoints;
+ const float spline_len = BKE_anim_path_get_length(cache);
+ const float *seg_accum_len = cache->anim_path_accum_length;
+
+ int max_seg_idx = BKE_anim_path_get_array_size(cache) - 1;
+
+ /* Convert our initial intersection point guess to a point index.
+ * If the curve was a straight line, then pointEnd would be the correct location.
+ * So make it our first initial guess.
+ */
+ const float guessed_len = *r_new_curve_pos * spline_len;
+
+ BLI_assert(prev_seg_idx >= 0);
+
+ int cur_seg_idx = prev_seg_idx;
+ while (cur_seg_idx < max_seg_idx && guessed_len > seg_accum_len[cur_seg_idx]) {
+ cur_seg_idx++;
+ }
+
+ int bp_idx = cur_seg_idx + 1;
+
+ bp = bp + bp_idx;
+ bool is_cyclic = bl->poly >= 0;
+ BevPoint *prev_bp = bp - 1;
+
+ /* Go to the next tessellated curve point until we cross to outside of the sphere. */
+ while (len_v3v3(head_pos, bp->vec) < sphere_radius) {
+ if (bp_idx > max_seg_idx) {
+ /* We are outside the defined curve. We will now extrapolate the intersection point. */
+ break;
+ }
+ prev_bp = bp;
+ if (is_cyclic && bp_idx == max_seg_idx) {
+ /* Wrap around to the start point.
+ * Don't set the bp_idx to zero here as we use it to get the segment index later.
+ */
+ bp = bl->bevpoints;
+ }
+ else {
+ bp++;
+ }
+ bp_idx++;
+ }
+
+ float isect_1[3], isect_2[3];
+
+ /* Calculate the intersection point. */
+ int ret = isect_line_sphere_v3(prev_bp->vec, bp->vec, head_pos, sphere_radius, isect_1, isect_2);
+
+ if (ret > 0) {
+ /* Because of how `isect_line_sphere_v3` works, we know that `isect_1` contains the
+ * intersection point we want. And it will always intersect as we go from inside to outside
+ * of the sphere.
+ */
+ copy_v3_v3(r_tail_pos, isect_1);
+ }
+ else {
+ /* Couldn't find an intersection point. This means that the floating point
+ * values are too small and thus the intersection check fails.
+ * So assume that the distance is so small that tail_pos == head_pos.
+ */
+ copy_v3_v3(r_tail_pos, head_pos);
+ }
+
+ cur_seg_idx = bp_idx - 2;
+ float prev_seg_len = 0;
+
+ if (cur_seg_idx < 0) {
+ cur_seg_idx = 0;
+ prev_seg_len = 0;
+ }
+ else {
+ prev_seg_len = seg_accum_len[cur_seg_idx];
+ }
+
+ /* Convert the point back into the 0-1 interpolation range. */
+ const float isect_seg_len = len_v3v3(prev_bp->vec, r_tail_pos);
+ const float frac = isect_seg_len / len_v3v3(prev_bp->vec, bp->vec);
+ *r_new_curve_pos = (prev_seg_len + isect_seg_len) / spline_len;
+
+ if (*r_new_curve_pos > 1.0f) {
+ *r_radius = bp->radius;
+ }
+ else {
+ *r_radius = (1.0f - frac) * prev_bp->radius + frac * bp->radius;
+ }
+
+ return cur_seg_idx;
+}
+
/* Evaluate spline IK for a given bone. */
static void splineik_evaluate_bone(
tSplineIK_Tree *tree, Object *ob, bPoseChannel *pchan, int index, tSplineIk_EvalState *state)
{
- bSplineIKConstraint *ikData = tree->ikData;
- float origHead[3], origTail[3], poseHead[3], poseTail[3], basePoseMat[3][3], poseMat[3][3];
- float splineVec[3], scaleFac, radius = 1.0f;
- float tailBlendFac = 0.0f;
+ bSplineIKConstraint *ik_data = tree->ik_data;
+
+ if (pchan->bone->length < FLT_EPSILON) {
+ /* Only move the bone position with zero length bones. */
+ float bone_pos[4], dir[3], rad;
+ BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, dir, NULL, &rad, NULL);
- mul_v3_m4v3(poseHead, state->locrot_offset, pchan->pose_head);
- mul_v3_m4v3(poseTail, state->locrot_offset, pchan->pose_tail);
+ apply_curve_transform(ik_data, ob, rad, bone_pos, &rad);
- copy_v3_v3(origHead, poseHead);
+ copy_v3_v3(pchan->pose_mat[3], bone_pos);
+ copy_v3_v3(pchan->pose_head, bone_pos);
+ copy_v3_v3(pchan->pose_tail, bone_pos);
+ pchan->flag |= POSE_DONE;
+ return;
+ }
+
+ float orig_head[3], orig_tail[3], pose_head[3], pose_tail[3];
+ float base_pose_mat[3][3], pose_mat[3][3];
+ float spline_vec[3], scale_fac, radius = 1.0f;
+ float tail_blend_fac = 0.0f;
+
+ mul_v3_m4v3(pose_head, state->locrot_offset, pchan->pose_head);
+ mul_v3_m4v3(pose_tail, state->locrot_offset, pchan->pose_tail);
+
+ copy_v3_v3(orig_head, pose_head);
- /* first, adjust the point positions on the curve */
+ /* First, adjust the point positions on the curve. */
float curveLen = tree->points[index] - tree->points[index + 1];
- float pointStart = state->curve_position;
- float poseScale = len_v3v3(poseHead, poseTail) / pchan->bone->length;
- float baseScale = 1.0f;
+ float bone_len = len_v3v3(pose_head, pose_tail);
+ float point_start = state->curve_position;
+ float pose_scale = bone_len / pchan->bone->length;
+ float base_scale = 1.0f;
- if (ikData->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
+ if (ik_data->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
/* Carry over the bone Y scale to the curve range. */
- baseScale = poseScale;
+ base_scale = pose_scale;
}
- float pointEnd = pointStart + curveLen * baseScale * state->curve_scale;
+ float point_end = point_start + curveLen * base_scale * state->curve_scale;
- state->curve_position = pointEnd;
+ state->curve_position = point_end;
- /* step 1: determine the positions for the endpoints of the bone */
- if (pointStart < 1.0f) {
+ /* Step 1: determine the positions for the endpoints of the bone. */
+ if (point_start < 1.0f) {
float vec[4], dir[3], rad;
+ radius = 0.0f;
- /* determine if the bone should still be affected by SplineIK */
- if (pointEnd >= 1.0f) {
- /* blending factor depends on the amount of the bone still left on the chain */
- tailBlendFac = (1.0f - pointStart) / (pointEnd - pointStart);
+ /* Calculate head position. */
+ if (point_start == 0.0f) {
+ /* Start of the path. We have no previous tail position to copy. */
+ BKE_where_on_path(ik_data->tar, point_start, vec, dir, NULL, &rad, NULL);
}
else {
- tailBlendFac = 1.0f;
+ copy_v3_v3(vec, state->prev_tail_loc);
+ rad = state->prev_tail_radius;
}
- /* tail endpoint */
- if (where_on_path(ikData->tar, pointEnd, vec, dir, NULL, &rad, NULL)) {
- /* apply curve's object-mode transforms to the position
- * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
- */
- if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
- mul_m4_v3(ikData->tar->obmat, vec);
+ radius = rad;
+ copy_v3_v3(pose_head, vec);
+ apply_curve_transform(ik_data, ob, rad, pose_head, &radius);
+
+ /* Calculate tail position. */
+ if (ik_data->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) {
+ float sphere_radius;
+
+ if (ik_data->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
+ sphere_radius = bone_len;
+ }
+ else {
+ /* Don't take bone scale into account. */
+ sphere_radius = pchan->bone->length;
}
- /* convert the position to pose-space, then store it */
- mul_m4_v3(ob->imat, vec);
- copy_v3_v3(poseTail, vec);
+ /* Calculate the tail position with sphere curve intersection. */
+ state->prev_tail_seg_idx = position_tail_on_spline(
+ ik_data, vec, sphere_radius, state->prev_tail_seg_idx, pose_tail, &point_end, &rad);
- /* set the new radius */
- radius = rad;
- }
+ state->prev_tail_radius = rad;
+ copy_v3_v3(state->prev_tail_loc, pose_tail);
- /* head endpoint */
- if (where_on_path(ikData->tar, pointStart, vec, dir, NULL, &rad, NULL)) {
- /* apply curve's object-mode transforms to the position
- * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
- */
- if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
- mul_m4_v3(ikData->tar->obmat, vec);
+ apply_curve_transform(ik_data, ob, rad, pose_tail, &radius);
+ state->curve_position = point_end;
+ }
+ else {
+ /* Scale to fit curve end position. */
+ if (BKE_where_on_path(ik_data->tar, point_end, vec, dir, NULL, &rad, NULL)) {
+ state->prev_tail_radius = rad;
+ copy_v3_v3(state->prev_tail_loc, vec);
+ copy_v3_v3(pose_tail, vec);
+ apply_curve_transform(ik_data, ob, rad, pose_tail, &radius);
}
+ }
- /* store the position, and convert it to pose space */
- mul_m4_v3(ob->imat, vec);
- copy_v3_v3(poseHead, vec);
-
- /* set the new radius (it should be the average value) */
- radius = (radius + rad) / 2;
+ /* Determine if the bone should still be affected by SplineIK.
+ * This makes it so that the bone slowly becomes poseable again the further it rolls off the
+ * curve. When the whole bone has rolled off the curve, the IK constraint will not influence it
+ * anymore.
+ */
+ if (point_end >= 1.0f) {
+ /* Blending factor depends on the amount of the bone still left on the chain. */
+ tail_blend_fac = (1.0f - point_start) / (point_end - point_start);
+ }
+ else {
+ tail_blend_fac = 1.0f;
}
}
@@ -335,11 +498,8 @@ static void splineik_evaluate_bone(
* - splineVec: the vector direction that the spline applies on the bone.
* - scaleFac: the factor that the bone length is scaled by to get the desired amount.
*/
- sub_v3_v3v3(splineVec, poseTail, poseHead);
- scaleFac = len_v3(splineVec) / pchan->bone->length;
-
- /* Extrapolate the full length of the bone as it rolls off the end of the curve. */
- scaleFac = (tailBlendFac < 1e-5f) ? baseScale : scaleFac / tailBlendFac;
+ sub_v3_v3v3(spline_vec, pose_tail, pose_head);
+ scale_fac = len_v3(spline_vec) / pchan->bone->length;
/* Step 3: compute the shortest rotation needed
* to map from the bone rotation to the current axis.
@@ -350,83 +510,102 @@ static void splineik_evaluate_bone(
float dmat[3][3], rmat[3][3];
float raxis[3], rangle;
- /* compute the raw rotation matrix from the bone's current matrix by extracting only the
- * orientation-relevant axes, and normalizing them
+ /* Compute the raw rotation matrix from the bone's current matrix by extracting only the
+ * orientation-relevant axes, and normalizing them.
*/
- mul_m3_m4m4(basePoseMat, state->locrot_offset, pchan->pose_mat);
- normalize_m3_m3(rmat, basePoseMat);
+ mul_m3_m4m4(base_pose_mat, state->locrot_offset, pchan->pose_mat);
+ normalize_m3_m3(rmat, base_pose_mat);
/* Also, normalize the orientation imposed by the bone,
* now that we've extracted the scale factor. */
- normalize_v3(splineVec);
+ normalize_v3(spline_vec);
+
+ /* Calculate smallest axis-angle rotation necessary for getting from the
+ * current orientation of the bone, to the spline-imposed direction.
+ */
+ cross_v3_v3v3(raxis, rmat[1], spline_vec);
- /* calculate smallest axis-angle rotation necessary for getting from the
- * current orientation of the bone, to the spline-imposed direction
+ /* Check if the old and new bone direction is parallel to each other.
+ * If they are, then 'raxis' should be near zero and we will have to get the rotation axis in
+ * some other way.
*/
- cross_v3_v3v3(raxis, rmat[1], splineVec);
+ float norm = normalize_v3(raxis);
+
+ if (norm < FLT_EPSILON) {
+ /* Can't use cross product! */
+ int order[3] = {0, 1, 2};
+ float tmp_axis[3];
+ zero_v3(tmp_axis);
+
+ axis_sort_v3(spline_vec, order);
+
+ /* Use the second largest axis as the basis for the rotation axis. */
+ tmp_axis[order[1]] = 1.0f;
+ cross_v3_v3v3(raxis, tmp_axis, spline_vec);
+ }
- rangle = dot_v3v3(rmat[1], splineVec);
+ rangle = dot_v3v3(rmat[1], spline_vec);
CLAMP(rangle, -1.0f, 1.0f);
rangle = acosf(rangle);
- /* multiply the magnitude of the angle by the influence of the constraint to
- * control the influence of the SplineIK effect
+ /* Multiply the magnitude of the angle by the influence of the constraint to
+ * control the influence of the SplineIK effect.
*/
- rangle *= tree->con->enforce * tailBlendFac;
+ rangle *= tree->con->enforce * tail_blend_fac;
- /* construct rotation matrix from the axis-angle rotation found above
- * - this call takes care to make sure that the axis provided is a unit vector first
+ /* Construct rotation matrix from the axis-angle rotation found above.
+ * - This call takes care to make sure that the axis provided is a unit vector first.
*/
axis_angle_to_mat3(dmat, raxis, rangle);
/* Combine these rotations so that the y-axis of the bone is now aligned as the
* spline dictates, while still maintaining roll control from the existing bone animation. */
- mul_m3_m3m3(poseMat, dmat, rmat);
+ mul_m3_m3m3(pose_mat, dmat, rmat);
- /* attempt to reduce shearing, though I doubt this'll really help too much now... */
- normalize_m3(poseMat);
+ /* Attempt to reduce shearing, though I doubt this'll really help too much now... */
+ normalize_m3(pose_mat);
- mul_m3_m3m3(basePoseMat, dmat, basePoseMat);
+ mul_m3_m3m3(base_pose_mat, dmat, base_pose_mat);
- /* apply rotation to the accumulated parent transform */
+ /* Apply rotation to the accumulated parent transform. */
mul_m4_m3m4(state->locrot_offset, dmat, state->locrot_offset);
}
- /* step 4: set the scaling factors for the axes */
+ /* Step 4: Set the scaling factors for the axes. */
/* Always multiply the y-axis by the scaling factor to get the correct length. */
- mul_v3_fl(poseMat[1], scaleFac);
+ mul_v3_fl(pose_mat[1], scale_fac);
/* After that, apply x/z scaling modes. */
- if (ikData->xzScaleMode != CONSTRAINT_SPLINEIK_XZS_NONE) {
+ if (ik_data->xzScaleMode != CONSTRAINT_SPLINEIK_XZS_NONE) {
/* First, apply the original scale if enabled. */
- if (ikData->xzScaleMode == CONSTRAINT_SPLINEIK_XZS_ORIGINAL ||
- (ikData->flag & CONSTRAINT_SPLINEIK_USE_ORIGINAL_SCALE) != 0) {
+ if (ik_data->xzScaleMode == CONSTRAINT_SPLINEIK_XZS_ORIGINAL ||
+ (ik_data->flag & CONSTRAINT_SPLINEIK_USE_ORIGINAL_SCALE) != 0) {
float scale;
- /* x-axis scale */
+ /* X-axis scale. */
scale = len_v3(pchan->pose_mat[0]);
- mul_v3_fl(poseMat[0], scale);
- /* z-axis scale */
+ mul_v3_fl(pose_mat[0], scale);
+ /* Z-axis scale. */
scale = len_v3(pchan->pose_mat[2]);
- mul_v3_fl(poseMat[2], scale);
+ mul_v3_fl(pose_mat[2], scale);
/* Adjust the scale factor used for volume preservation
* to consider the pre-IK scaling as the initial volume. */
- scaleFac /= poseScale;
+ scale_fac /= pose_scale;
}
/* Apply volume preservation. */
- switch (ikData->xzScaleMode) {
+ switch (ik_data->xzScaleMode) {
case CONSTRAINT_SPLINEIK_XZS_INVERSE: {
- /* old 'volume preservation' method using the inverse scale */
+ /* Old 'volume preservation' method using the inverse scale. */
float scale;
- /* calculate volume preservation factor which is
- * basically the inverse of the y-scaling factor
+ /* Calculate volume preservation factor which is
+ * basically the inverse of the y-scaling factor.
*/
- if (fabsf(scaleFac) != 0.0f) {
- scale = 1.0f / fabsf(scaleFac);
+ if (fabsf(scale_fac) != 0.0f) {
+ scale = 1.0f / fabsf(scale_fac);
/* We need to clamp this within sensible values. */
/* NOTE: these should be fine for now, but should get sanitized in future. */
@@ -436,56 +615,56 @@ static void splineik_evaluate_bone(
scale = 1.0f;
}
- /* apply the scaling */
- mul_v3_fl(poseMat[0], scale);
- mul_v3_fl(poseMat[2], scale);
+ /* Apply the scaling. */
+ mul_v3_fl(pose_mat[0], scale);
+ mul_v3_fl(pose_mat[2], scale);
break;
}
case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC: {
- /* improved volume preservation based on the Stretch To constraint */
+ /* Improved volume preservation based on the Stretch To constraint. */
float final_scale;
- /* as the basis for volume preservation, we use the inverse scale factor... */
- if (fabsf(scaleFac) != 0.0f) {
- /* NOTE: The method here is taken wholesale from the Stretch To constraint */
- float bulge = powf(1.0f / fabsf(scaleFac), ikData->bulge);
+ /* As the basis for volume preservation, we use the inverse scale factor... */
+ if (fabsf(scale_fac) != 0.0f) {
+ /* NOTE: The method here is taken wholesale from the Stretch To constraint. */
+ float bulge = powf(1.0f / fabsf(scale_fac), ik_data->bulge);
if (bulge > 1.0f) {
- if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) {
- float bulge_max = max_ff(ikData->bulge_max, 1.0f);
+ if (ik_data->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) {
+ float bulge_max = max_ff(ik_data->bulge_max, 1.0f);
float hard = min_ff(bulge, bulge_max);
float range = bulge_max - 1.0f;
float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2;
- bulge = interpf(soft, hard, ikData->bulge_smooth);
+ bulge = interpf(soft, hard, ik_data->bulge_smooth);
}
}
if (bulge < 1.0f) {
- if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) {
- float bulge_min = CLAMPIS(ikData->bulge_min, 0.0f, 1.0f);
+ if (ik_data->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) {
+ float bulge_min = CLAMPIS(ik_data->bulge_min, 0.0f, 1.0f);
float hard = max_ff(bulge, bulge_min);
float range = 1.0f - bulge_min;
float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2;
- bulge = interpf(soft, hard, ikData->bulge_smooth);
+ bulge = interpf(soft, hard, ik_data->bulge_smooth);
}
}
- /* compute scale factor for xz axes from this value */
+ /* Compute scale factor for xz axes from this value. */
final_scale = sqrtf(bulge);
}
else {
- /* no scaling, so scale factor is simple */
+ /* No scaling, so scale factor is simple. */
final_scale = 1.0f;
}
/* Apply the scaling (assuming normalized scale). */
- mul_v3_fl(poseMat[0], final_scale);
- mul_v3_fl(poseMat[2], final_scale);
+ mul_v3_fl(pose_mat[0], final_scale);
+ mul_v3_fl(pose_mat[2], final_scale);
break;
}
}
@@ -494,49 +673,49 @@ static void splineik_evaluate_bone(
/* Finally, multiply the x and z scaling by the radius of the curve too,
* to allow automatic scales to get tweaked still.
*/
- if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) {
- mul_v3_fl(poseMat[0], radius);
- mul_v3_fl(poseMat[2], radius);
+ if ((ik_data->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) {
+ mul_v3_fl(pose_mat[0], radius);
+ mul_v3_fl(pose_mat[2], radius);
}
/* Blend the scaling of the matrix according to the influence. */
- sub_m3_m3m3(poseMat, poseMat, basePoseMat);
- madd_m3_m3m3fl(poseMat, basePoseMat, poseMat, tree->con->enforce * tailBlendFac);
+ sub_m3_m3m3(pose_mat, pose_mat, base_pose_mat);
+ madd_m3_m3m3fl(pose_mat, base_pose_mat, pose_mat, tree->con->enforce * tail_blend_fac);
- /* step 5: set the location of the bone in the matrix */
- if (ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) {
- /* when the 'no-root' option is affected, the chain can retain
- * the shape but be moved elsewhere
+ /* Step 5: Set the location of the bone in the matrix. */
+ if (ik_data->flag & CONSTRAINT_SPLINEIK_NO_ROOT) {
+ /* When the 'no-root' option is affected, the chain can retain
+ * the shape but be moved elsewhere.
*/
- copy_v3_v3(poseHead, origHead);
+ copy_v3_v3(pose_head, orig_head);
}
else if (tree->con->enforce < 1.0f) {
- /* when the influence is too low
- * - blend the positions for the 'root' bone
- * - stick to the parent for any other
+ /* When the influence is too low:
+ * - Blend the positions for the 'root' bone.
+ * - Stick to the parent for any other.
*/
if (index < tree->chainlen - 1) {
- copy_v3_v3(poseHead, origHead);
+ copy_v3_v3(pose_head, orig_head);
}
else {
- interp_v3_v3v3(poseHead, origHead, poseHead, tree->con->enforce);
+ interp_v3_v3v3(pose_head, orig_head, pose_head, tree->con->enforce);
}
}
- /* finally, store the new transform */
- copy_m4_m3(pchan->pose_mat, poseMat);
- copy_v3_v3(pchan->pose_mat[3], poseHead);
- copy_v3_v3(pchan->pose_head, poseHead);
+ /* Finally, store the new transform. */
+ copy_m4_m3(pchan->pose_mat, pose_mat);
+ copy_v3_v3(pchan->pose_mat[3], pose_head);
+ copy_v3_v3(pchan->pose_head, pose_head);
- mul_v3_mat3_m4v3(origTail, state->locrot_offset, pchan->pose_tail);
+ mul_v3_mat3_m4v3(orig_tail, state->locrot_offset, pchan->pose_tail);
- /* recalculate tail, as it's now outdated after the head gets adjusted above! */
+ /* Recalculate tail, as it's now outdated after the head gets adjusted above! */
BKE_pose_where_is_bone_tail(pchan);
- /* update the offset in the accumulated parent transform */
- sub_v3_v3v3(state->locrot_offset[3], pchan->pose_tail, origTail);
+ /* Update the offset in the accumulated parent transform. */
+ sub_v3_v3v3(state->locrot_offset[3], pchan->pose_tail, orig_tail);
- /* done! */
+ /* Done! */
pchan->flag |= POSE_DONE;
}
@@ -559,8 +738,8 @@ static void splineik_execute_tree(
if (splineik_evaluate_init(tree, &state)) {
/* Walk over each bone in the chain, calculating the effects of spline IK
- * - the chain is traversed in the opposite order to storage order (i.e. parent to children)
- * so that dependencies are correct
+ * - the chain is traversed in the opposite order to storage order
+ * (i.e. parent to children) so that dependencies are correct
*/
for (int i = tree->chainlen - 1; i >= 0; i--) {
bPoseChannel *pchan = tree->chain[i];
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index 05da47aed8e..8bbb3014dac 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -34,7 +34,7 @@
#include "CLG_log.h"
-#include "NOD_node_tree_multi_function.hh"
+#include "NOD_type_conversions.hh"
#include "attribute_access_intern.hh"
@@ -44,194 +44,13 @@ using blender::float3;
using blender::Set;
using blender::StringRef;
using blender::StringRefNull;
-using blender::bke::ReadAttributePtr;
-using blender::bke::WriteAttributePtr;
using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_SingleValue;
namespace blender::bke {
-/* -------------------------------------------------------------------- */
-/** \name Attribute Accessor implementations
- * \{ */
-
-ReadAttribute::~ReadAttribute()
-{
- if (array_is_temporary_ && array_buffer_ != nullptr) {
- cpp_type_.destruct_n(array_buffer_, size_);
- MEM_freeN(array_buffer_);
- }
-}
-
-fn::GSpan ReadAttribute::get_span() const
-{
- if (size_ == 0) {
- return fn::GSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- std::lock_guard lock{span_mutex_};
- if (array_buffer_ == nullptr) {
- this->initialize_span();
- }
- }
- return fn::GSpan(cpp_type_, array_buffer_, size_);
-}
-
-void ReadAttribute::initialize_span() const
-{
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- for (const int i : IndexRange(size_)) {
- this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
-}
-
-WriteAttribute::~WriteAttribute()
-{
- if (array_should_be_applied_) {
- CLOG_ERROR(&LOG, "Forgot to call apply_span.");
- }
- if (array_is_temporary_ && array_buffer_ != nullptr) {
- cpp_type_.destruct_n(array_buffer_, size_);
- MEM_freeN(array_buffer_);
- }
-}
-
-/**
- * Get a mutable span that can be modified. When all modifications to the attribute are done,
- * #apply_span should be called. */
-fn::GMutableSpan WriteAttribute::get_span()
-{
- if (size_ == 0) {
- return fn::GMutableSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- this->initialize_span(false);
- }
- array_should_be_applied_ = true;
- return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
-}
-
-fn::GMutableSpan WriteAttribute::get_span_for_write_only()
-{
- if (size_ == 0) {
- return fn::GMutableSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- this->initialize_span(true);
- }
- array_should_be_applied_ = true;
- return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
-}
-
-void WriteAttribute::initialize_span(const bool write_only)
-{
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- if (write_only) {
- /* This does nothing for trivial types, but is necessary for general correctness. */
- cpp_type_.construct_default_n(array_buffer_, size_);
- }
- else {
- for (const int i : IndexRange(size_)) {
- this->get(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
- }
-}
-
-void WriteAttribute::apply_span()
-{
- this->apply_span_if_necessary();
- array_should_be_applied_ = false;
-}
-
-void WriteAttribute::apply_span_if_necessary()
-{
- /* Only works when the span has been initialized beforehand. */
- BLI_assert(array_buffer_ != nullptr);
-
- const int element_size = cpp_type_.size();
- for (const int i : IndexRange(size_)) {
- this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
-}
-
-/* This is used by the #OutputAttributePtr class. */
-class TemporaryWriteAttribute final : public WriteAttribute {
- public:
- GMutableSpan data;
- GeometryComponent &component;
- std::string final_name;
-
- TemporaryWriteAttribute(AttributeDomain domain,
- GMutableSpan data,
- GeometryComponent &component,
- std::string final_name)
- : WriteAttribute(domain, data.type(), data.size()),
- data(data),
- component(component),
- final_name(std::move(final_name))
- {
- }
-
- ~TemporaryWriteAttribute() override
- {
- if (data.data() != nullptr) {
- cpp_type_.destruct_n(data.data(), data.size());
- MEM_freeN(data.data());
- }
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- data.type().copy_to_uninitialized(data[index], r_value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data.type().copy_to_initialized(value, data[index]);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
-class ConvertedReadAttribute final : public ReadAttribute {
- private:
- const CPPType &from_type_;
- const CPPType &to_type_;
- ReadAttributePtr base_attribute_;
- const nodes::DataTypeConversions &conversions_;
-
- public:
- ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type)
- : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()),
- from_type_(base_attribute->cpp_type()),
- to_type_(to_type),
- base_attribute_(std::move(base_attribute)),
- conversions_(nodes::get_implicit_type_conversions())
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
- base_attribute_->get(index, buffer);
- conversions_.convert(from_type_, to_type_, buffer, r_value);
- }
-};
-
-/** \} */
-
const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
{
switch (type) {
@@ -244,7 +63,7 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty
case CD_PROP_INT32:
return &CPPType::get<int>();
case CD_PROP_COLOR:
- return &CPPType::get<Color4f>();
+ return &CPPType::get<ColorGeometry4f>();
case CD_PROP_BOOL:
return &CPPType::get<bool>();
default:
@@ -267,7 +86,7 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
if (type.is<int>()) {
return CD_PROP_INT32;
}
- if (type.is<Color4f>()) {
+ if (type.is<ColorGeometry4f>()) {
return CD_PROP_COLOR;
}
if (type.is<bool>()) {
@@ -327,10 +146,8 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_
static int attribute_domain_priority(const AttributeDomain domain)
{
switch (domain) {
-#if 0
case ATTR_DOMAIN_CURVE:
return 0;
-#endif
case ATTR_DOMAIN_FACE:
return 1;
case ATTR_DOMAIN_EDGE:
@@ -366,7 +183,27 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
return highest_priority_domain;
}
-ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
+void OutputAttribute::save()
+{
+ save_has_been_called_ = true;
+ if (optional_span_varray_.has_value()) {
+ optional_span_varray_->save();
+ }
+ if (save_) {
+ save_(*this);
+ }
+}
+
+OutputAttribute::~OutputAttribute()
+{
+ if (!save_has_been_called_) {
+ if (varray_) {
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ }
+ }
+}
+
+GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read(
const GeometryComponent &component) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -382,7 +219,7 @@ ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
return as_read_attribute_(data, domain_size);
}
-WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write(
+GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
GeometryComponent &component) const
{
if (writable_ != Writable) {
@@ -428,7 +265,43 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co
return delete_success;
}
-bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) const
+static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data,
+ const CustomDataType data_type,
+ const int domain_size,
+ const AttributeInit &initializer)
+{
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::VArray: {
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ if (data == nullptr) {
+ return false;
+ }
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
+ return true;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *data = CustomData_add_layer(
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_size);
+ if (data == nullptr) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
+bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
+ const AttributeInit &initializer) const
{
if (createable_ != Creatable) {
return false;
@@ -441,10 +314,10 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) co
/* Exists already. */
return false;
}
+
const int domain_size = component.attribute_domain_size(domain_);
- const void *data = CustomData_add_layer(
- custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size);
- const bool success = data != nullptr;
+ const bool success = add_custom_data_layer_from_attribute_init(
+ *custom_data, stored_type_, domain_size, initializer);
if (success) {
custom_data_access_.update_custom_data_pointers(component);
}
@@ -461,7 +334,7 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component)
return data != nullptr;
}
-ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
+ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -484,7 +357,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
case CD_PROP_INT32:
return this->layer_to_read_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
- return this->layer_to_read_attribute<Color4f>(layer, domain_size);
+ return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_read_attribute<bool>(layer, domain_size);
default:
@@ -494,7 +367,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
return {};
}
-WriteAttributePtr CustomDataAttributeProvider::try_get_for_write(
+WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
@@ -518,7 +391,7 @@ WriteAttributePtr CustomDataAttributeProvider::try_get_for_write(
case CD_PROP_INT32:
return this->layer_to_write_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
- return this->layer_to_write_attribute<Color4f>(layer, domain_size);
+ return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_write_attribute<bool>(layer, domain_size);
default:
@@ -546,10 +419,52 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
return false;
}
+static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name,
+ CustomData &custom_data,
+ const CustomDataType data_type,
+ const int domain_size,
+ const AttributeInit &initializer)
+{
+ char attribute_name_c[MAX_NAME];
+ attribute_name.copy(attribute_name_c);
+
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::VArray: {
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ if (data == nullptr) {
+ return false;
+ }
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
+ return true;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c);
+ if (data == nullptr) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type) const
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const
{
if (domain_ != domain) {
return false;
@@ -567,10 +482,8 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
}
}
const int domain_size = component.attribute_domain_size(domain_);
- char attribute_name_c[MAX_NAME];
- attribute_name.copy(attribute_name_c);
- CustomData_add_layer_named(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ add_named_custom_data_layer_from_attribute_init(
+ attribute_name, *custom_data, data_type, domain_size, initializer);
return true;
}
@@ -593,7 +506,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
return true;
}
-ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
+ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -604,14 +517,14 @@ ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
if (layer.type == stored_type_) {
if (layer.name == attribute_name) {
const int domain_size = component.attribute_domain_size(domain_);
- return as_read_attribute_(layer.data, domain_size);
+ return {as_read_attribute_(layer.data, domain_size), domain_};
}
}
}
return {};
}
-WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
+WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
@@ -628,7 +541,7 @@ WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
if (data_old != data_new) {
custom_data_access_.update_custom_data_pointers(component);
}
- return as_write_attribute_(layer.data, domain_size);
+ return {as_write_attribute_(layer.data, domain_size), domain_};
}
}
}
@@ -680,6 +593,142 @@ void NamedLegacyCustomDataProvider::foreach_domain(
callback(domain_);
}
+CustomDataAttributes::CustomDataAttributes()
+{
+ CustomData_reset(&data);
+ size_ = 0;
+}
+
+CustomDataAttributes::~CustomDataAttributes()
+{
+ CustomData_free(&data, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
+{
+ size_ = other.size_;
+ CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
+{
+ size_ = other.size_;
+ data = other.data;
+ CustomData_reset(&other.data);
+}
+
+CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes &other)
+{
+ if (this != &other) {
+ CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, other.size_);
+ size_ = other.size_;
+ }
+
+ return *this;
+}
+
+std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
+{
+ BLI_assert(size_ != 0);
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+/**
+ * Return a virtual array for a stored attribute, or a single value virtual array with the default
+ * value if the attribute doesn't exist. If no default value is provided, the default value for the
+ * type will be used.
+ */
+GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name,
+ const CustomDataType data_type,
+ const void *default_value) const
+{
+ const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+
+ std::optional<GSpan> attribute = this->get_for_read(name);
+ if (!attribute) {
+ const int domain_size = this->size_;
+ return std::make_unique<GVArray_For_SingleValue>(
+ *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value);
+ }
+
+ if (attribute->type() == *type) {
+ return std::make_unique<GVArray_For_GSpan>(*attribute);
+ }
+ const blender::nodes::DataTypeConversions &conversions =
+ blender::nodes::get_implicit_type_conversions();
+ return conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), *type);
+}
+
+std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
+{
+ /* If this assert hits, it most likely means that #reallocate was not called at some point. */
+ BLI_assert(size_ != 0);
+ for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GMutableSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type)
+{
+ char name_c[MAX_NAME];
+ name.copy(name_c);
+ void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::create_by_move(const blender::StringRef name,
+ const CustomDataType data_type,
+ void *buffer)
+{
+ char name_c[MAX_NAME];
+ name.copy(name_c);
+ void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::remove(const blender::StringRef name)
+{
+ bool result = false;
+ for (const int i : IndexRange(data.totlayer)) {
+ const CustomDataLayer &layer = data.layers[i];
+ if (layer.name == name) {
+ CustomData_free_layer(&data, layer.type, size_, i);
+ result = true;
+ }
+ }
+ return result;
+}
+
+void CustomDataAttributes::reallocate(const int size)
+{
+ size_ = size;
+ CustomData_realloc(&data, size);
+}
+
+bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const
+{
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
+ if (!callback(layer.name, meta_data)) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace blender::bke
/* -------------------------------------------------------------------- */
@@ -706,7 +755,17 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain
return 0;
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const
+{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ return providers->builtin_attribute_providers().contains_as(attribute_name);
+}
+
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name) const
{
using namespace blender::bke;
@@ -717,11 +776,11 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
- return builtin_provider->try_get_for_read(*this);
+ return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
+ ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
if (attribute) {
return attribute;
}
@@ -729,16 +788,19 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
return {};
}
-ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(
- ReadAttributePtr attribute, const AttributeDomain new_domain) const
+std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
- if (attribute && attribute->domain() == new_domain) {
- return attribute;
+ if (from_domain == to_domain) {
+ return varray;
}
return {};
}
-WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name)
+blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write(
+ const StringRef attribute_name)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
@@ -748,11 +810,11 @@ WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
- return builtin_provider->try_get_for_write(*this);
+ return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
+ WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
if (attribute) {
return attribute;
}
@@ -782,7 +844,8 @@ bool GeometryComponent::attribute_try_delete(const StringRef attribute_name)
bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type)
+ const CustomDataType data_type,
+ const AttributeInit &initializer)
{
using namespace blender::bke;
if (attribute_name.is_empty()) {
@@ -801,17 +864,36 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
if (builtin_provider->data_type() != data_type) {
return false;
}
- return builtin_provider->try_create(*this);
+ return builtin_provider->try_create(*this, initializer);
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) {
+ if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) {
return true;
}
}
return false;
}
+bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
+ const AttributeInit &initializer)
+{
+ using namespace blender::bke;
+ if (attribute_name.is_empty()) {
+ return false;
+ }
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ const BuiltinAttributeProvider *builtin_provider =
+ providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
+ if (builtin_provider == nullptr) {
+ return false;
+ }
+ return builtin_provider->try_create(*this, initializer);
+}
+
Set<std::string> GeometryComponent::attribute_names() const
{
Set<std::string> attributes;
@@ -822,12 +904,16 @@ Set<std::string> GeometryComponent::attribute_names() const
return attributes;
}
-void GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const
+/**
+ * \return False if the callback explicitly returned false at any point, otherwise true,
+ * meaning the callback made it all the way through.
+ */
+bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
if (providers == nullptr) {
- return;
+ return true;
}
/* Keep track handled attribute names to make sure that we do not return the same name twice. */
@@ -838,7 +924,7 @@ void GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
if (provider->exists(*this)) {
AttributeMetaData meta_data{provider->domain(), provider->data_type()};
if (!callback(provider->name(), meta_data)) {
- return;
+ return false;
}
handled_attribute_names.add_new(provider->name());
}
@@ -852,271 +938,292 @@ void GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
return true;
});
if (!continue_loop) {
- return;
+ return false;
}
}
+
+ return true;
}
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup 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)
+std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
+ const StringRef attribute_name) const
{
- const blender::fn::CPPType &from_type = attribute->cpp_type();
- if (from_type == to_type) {
- return attribute;
- }
+ std::optional<AttributeMetaData> result{std::nullopt};
+ this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (attribute_name == name) {
+ result = meta_data;
+ return false;
+ }
+ return true;
+ });
+ return result;
+}
+static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
+ std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
+{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
- if (!conversions.is_convertible(from_type, to_type)) {
- return {};
- }
-
- return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type);
+ return conversions.try_convert(std::move(varray), to_type);
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup 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) {
+ std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
+ if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
+ varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
+ if (!varray) {
return {};
}
}
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
- if (attribute->cpp_type() != *cpp_type) {
- attribute = try_adapt_data_type(std::move(attribute), *cpp_type);
- if (!attribute) {
+ if (varray->type() != *cpp_type) {
+ varray = try_adapt_data_type(std::move(varray), *cpp_type);
+ if (!varray) {
return {};
}
}
- return attribute;
+ return varray;
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name,
- const AttributeDomain domain) const
+std::unique_ptr<blender::bke::GVArray> 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);
+ blender::bke::ReadAttributeLookup 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 {};
- }
+ if (attribute.domain != domain) {
+ return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain);
}
- return attribute;
+ return std::move(attribute.varray);
}
-ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value) const
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const CustomDataType data_type) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type);
- if (attribute) {
- return attribute;
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
+ if (!attribute) {
+ return {};
}
- return this->attribute_get_constant_for_read(domain, data_type, default_value);
-}
-
-blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read(
- const AttributeDomain domain, const CustomDataType data_type, const void *value) const
-{
- BLI_assert(this->attribute_domain_supported(domain));
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
- if (value == nullptr) {
- value = cpp_type->default_value();
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(type != nullptr);
+ if (attribute.varray->type() == *type) {
+ return attribute;
}
- const int domain_size = this->attribute_domain_size(domain);
- return std::make_unique<blender::bke::ConstantReadAttribute>(
- domain, domain_size, *cpp_type, value);
+ const blender::nodes::DataTypeConversions &conversions =
+ blender::nodes::get_implicit_type_conversions();
+ return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain};
}
-blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted(
+std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
+ const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType in_data_type,
- const CustomDataType out_data_type,
- const void *value) const
+ const CustomDataType data_type,
+ const void *default_value) const
{
- BLI_assert(this->attribute_domain_supported(domain));
- if (value == nullptr || in_data_type == out_data_type) {
- return this->attribute_get_constant_for_read(domain, out_data_type, value);
+ std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read(
+ attribute_name, domain, data_type);
+ if (varray) {
+ return varray;
+ }
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ if (default_value == nullptr) {
+ default_value = type->default_value();
}
-
- const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type(
- in_data_type);
- const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type(
- out_data_type);
- BLI_assert(in_cpp_type != nullptr);
- BLI_assert(out_cpp_type != nullptr);
-
- const blender::nodes::DataTypeConversions &conversions =
- blender::nodes::get_implicit_type_conversions();
- BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type));
-
- void *out_value = alloca(out_cpp_type->size());
- conversions.convert(*in_cpp_type, *out_cpp_type, value, out_value);
-
const int domain_size = this->attribute_domain_size(domain);
- blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>(
- domain, domain_size, *out_cpp_type, out_value);
-
- out_cpp_type->destruct(out_value);
- return attribute;
+ return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
}
-OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value)
-{
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
-
- WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name);
+class GVMutableAttribute_For_OutputAttribute
+ : public blender::fn::GVMutableArray_For_GMutableSpan {
+ public:
+ GeometryComponent *component;
+ std::string final_name;
- /* If the attribute doesn't exist, make a new one with the correct type. */
- if (!attribute) {
- this->attribute_try_create(attribute_name, domain, data_type);
- attribute = this->attribute_try_get_for_write(attribute_name);
- if (attribute && default_value != nullptr) {
- void *data = attribute->get_span_for_write_only().data();
- cpp_type->fill_initialized(default_value, data, attribute->size());
- attribute->apply_span();
- }
- return OutputAttributePtr(std::move(attribute));
+ GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
+ GeometryComponent &component,
+ std::string final_name)
+ : blender::fn::GVMutableArray_For_GMutableSpan(data),
+ component(&component),
+ final_name(std::move(final_name))
+ {
}
- /* If an existing attribute has a matching domain and type, just use that. */
- if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) {
- return OutputAttributePtr(std::move(attribute));
+ ~GVMutableAttribute_For_OutputAttribute() override
+ {
+ type_->destruct_n(data_, size_);
+ MEM_freeN(data_);
}
+};
- /* Otherwise create a temporary buffer to use before saving the new attribute. */
- return OutputAttributePtr(*this, domain, attribute_name, data_type);
-}
-
-/* Construct from an attribute that already exists in the geometry component. */
-OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute)
- : attribute_(std::move(attribute))
+static void save_output_attribute(blender::bke::OutputAttribute &output_attribute)
{
-}
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
-/* Construct a temporary attribute that has to replace an existing one later on. */
-OutputAttributePtr::OutputAttributePtr(GeometryComponent &component,
- AttributeDomain domain,
- std::string final_name,
- CustomDataType data_type)
-{
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
+ GVMutableAttribute_For_OutputAttribute &varray =
+ dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray());
- const int domain_size = component.attribute_domain_size(domain);
- void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__);
- GMutableSpan new_span{*cpp_type, buffer, domain_size};
+ GeometryComponent &component = *varray.component;
+ const StringRefNull name = varray.final_name;
+ const AttributeDomain domain = output_attribute.domain();
+ const CustomDataType data_type = output_attribute.custom_data_type();
+ const CPPType &cpp_type = output_attribute.cpp_type();
- /* Copy converted values from conflicting attribute, in case the value is read.
- * TODO: An optimization could be to not do this, when the caller says that the attribute will
- * only be written. */
- ReadAttributePtr src_attribute = component.attribute_get_for_read(
- final_name, domain, data_type, nullptr);
- for (const int i : blender::IndexRange(domain_size)) {
- src_attribute->get(i, new_span[i]);
+ component.attribute_try_delete(name);
+ if (!component.attribute_try_create(
+ varray.final_name, domain, data_type, AttributeInitDefault())) {
+ CLOG_WARN(&LOG,
+ "Could not create the '%s' attribute with type '%s'.",
+ name.c_str(),
+ cpp_type.name().c_str());
+ return;
+ }
+ WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
+ BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
+ for (const int i : IndexRange(varray.size())) {
+ varray.get(i, buffer);
+ write_attribute.varray->set_by_relocate(i, buffer);
}
-
- attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>(
- domain, new_span, component, std::move(final_name));
}
-/* Store the computed attribute. If it was stored from the beginning already, nothing is done. This
- * might delete another attribute with the same name. */
-void OutputAttributePtr::save()
+static blender::bke::OutputAttribute create_output_attribute(
+ GeometryComponent &component,
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const bool ignore_old_values,
+ const void *default_value)
{
- if (!attribute_) {
- CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore.");
- return;
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
+
+ if (attribute_name.is_empty()) {
+ return {};
}
- blender::bke::TemporaryWriteAttribute *attribute =
- dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get());
+ const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+ const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
- if (attribute == nullptr) {
- /* The attribute is saved already. */
- attribute_.reset();
- return;
+ if (component.attribute_is_builtin(attribute_name)) {
+ WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ if (default_value) {
+ const int64_t domain_size = component.attribute_domain_size(domain);
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create_builtin(attribute_name,
+ AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
+ }
+ attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ /* Builtin attribute does not exist and can't be created. */
+ return {};
+ }
+ }
+ if (attribute.domain != domain) {
+ /* Builtin attribute is on different domain. */
+ return {};
+ }
+ GVMutableArrayPtr varray = std::move(attribute.varray);
+ if (varray->type() == *cpp_type) {
+ /* Builtin attribute matches exactly. */
+ return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
+ }
+ /* Builtin attribute is on the same domain but has a different data type. */
+ varray = conversions.try_convert(std::move(varray), *cpp_type);
+ return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
}
- StringRefNull name = attribute->final_name;
- const blender::fn::CPPType &cpp_type = attribute->cpp_type();
+ const int domain_size = component.attribute_domain_size(domain);
- /* Delete an existing attribute with the same name if necessary. */
- attribute->component.attribute_try_delete(name);
+ WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ if (default_value) {
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create(
+ attribute_name, domain, data_type, AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault());
+ }
- if (!attribute->component.attribute_try_create(
- name, attribute_->domain(), attribute_->custom_data_type())) {
- /* Cannot create the target attribute for some reason. */
- CLOG_WARN(&LOG,
- "Creating the '%s' attribute with type '%s' failed.",
- name.c_str(),
- cpp_type.name().c_str());
- attribute_.reset();
- return;
+ attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ /* Can't create the attribute. */
+ return {};
+ }
+ }
+ if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
+ /* Existing generic attribute matches exactly. */
+ return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
}
- WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name);
-
- GMutableSpan temp_span = attribute->data;
- GMutableSpan new_span = new_attribute->get_span_for_write_only();
- BLI_assert(temp_span.size() == new_span.size());
-
- /* Currently we copy over the attribute. In the future we want to reuse the buffer. */
- cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size());
- new_attribute->apply_span();
+ /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
+ * attribute after processing is done. */
+ void *data = MEM_mallocN_aligned(
+ cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
+ if (ignore_old_values) {
+ /* This does nothing for trivially constructible types, but is necessary for correctness. */
+ cpp_type->construct_default_n(data, domain);
+ }
+ else {
+ /* Fill the temporary array with values from the existing attribute. */
+ GVArrayPtr old_varray = component.attribute_get_for_read(
+ attribute_name, domain, data_type, default_value);
+ old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
+ }
+ GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>(
+ GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name);
- attribute_.reset();
+ return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
}
-OutputAttributePtr::~OutputAttributePtr()
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output(
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value)
{
- if (attribute_) {
- CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save.");
- }
+ return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value);
}
-/* Utility function to call #apply_span and #save in the right order. */
-void OutputAttributePtr::apply_span_and_save()
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
{
- BLI_assert(attribute_);
- attribute_->apply_span();
- this->save();
+ return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
}
-
-/** \} */
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 806d10e9e89..b3a795faa30 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -24,166 +24,8 @@
namespace blender::bke {
-class ConstantReadAttribute final : public ReadAttribute {
- private:
- void *value_;
-
- public:
- ConstantReadAttribute(AttributeDomain domain,
- const int64_t size,
- const CPPType &type,
- const void *value)
- : ReadAttribute(domain, type, size)
- {
- value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
- type.copy_to_uninitialized(value, value_);
- }
-
- ~ConstantReadAttribute() override
- {
- this->cpp_type_.destruct(value_);
- MEM_freeN(value_);
- }
-
- void get_internal(const int64_t UNUSED(index), void *r_value) const override
- {
- this->cpp_type_.copy_to_uninitialized(value_, r_value);
- }
-
- void initialize_span() const override
- {
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
- }
-};
-
-template<typename T> class ArrayReadAttribute final : public ReadAttribute {
- private:
- Span<T> data_;
-
- public:
- ArrayReadAttribute(AttributeDomain domain, Span<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
- private:
- Array<T> data_;
-
- public:
- OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
-class DerivedArrayReadAttribute final : public ReadAttribute {
- private:
- Span<StructT> data_;
-
- public:
- DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
- : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-};
-
-template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<T> data_;
-
- public:
- ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
- : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data_[index] = *reinterpret_cast<const T *>(value);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data_.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
-template<typename StructT,
- typename ElemT,
- ElemT (*GetFunc)(const StructT &),
- void (*SetFunc)(StructT &, const ElemT &)>
-class DerivedArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<StructT> data_;
-
- public:
- DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
- : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- StructT &struct_value = data_[index];
- const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
- SetFunc(struct_value, typed_value);
- }
-};
+using fn::GVArrayPtr;
+using fn::GVMutableArrayPtr;
/**
* Utility to group together multiple functions that are used to access custom data on geometry
@@ -244,10 +86,11 @@ class BuiltinAttributeProvider {
{
}
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
+ virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
+ virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0;
virtual bool try_delete(GeometryComponent &component) const = 0;
- virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
+ virtual bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const = 0;
virtual bool exists(const GeometryComponent &component) const = 0;
StringRefNull name() const
@@ -272,15 +115,16 @@ class BuiltinAttributeProvider {
*/
class DynamicAttributesProvider {
public:
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const = 0;
+ virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
+ virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const StringRef UNUSED(attribute_name),
const AttributeDomain UNUSED(domain),
- const CustomDataType UNUSED(data_type)) const
+ const CustomDataType UNUSED(data_type),
+ const AttributeInit &UNUSED(initializer)) const
{
/* Some providers should not create new attributes. */
return false;
@@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final;
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type) const final;
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
template<typename T>
- ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
- const int domain_size) const
+ ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
+ const int domain_size) const
{
- return std::make_unique<ArrayReadAttribute<T>>(
- domain_, Span(static_cast<const T *>(layer.data), domain_size));
+ return {std::make_unique<fn::GVArray_For_Span<T>>(
+ Span(static_cast<const T *>(layer.data), domain_size)),
+ domain_};
}
template<typename T>
- WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
+ WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
+ const int domain_size) const
{
- return std::make_unique<ArrayWriteAttribute<T>>(
- domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
+ return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
+ MutableSpan(static_cast<T *>(layer.data), domain_size)),
+ domain_};
}
bool type_is_supported(CustomDataType data_type) const
@@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
*/
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
private:
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
const AttributeDomain domain_;
const CustomDataType attribute_type_;
const CustomDataType stored_type_;
@@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final;
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
* the #MVert struct, but is exposed as float3 attribute.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
using UpdateOnRead = void (*)(const GeometryComponent &component);
using UpdateOnWrite = void (*)(GeometryComponent &component);
const CustomDataType stored_type_;
@@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component) const final;
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final;
bool try_delete(GeometryComponent &component) const final;
- bool try_create(GeometryComponent &component) const final;
+ bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
bool exists(const GeometryComponent &component) const final;
};
diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc
index 4ff3a6ceff5..5cdf329effb 100644
--- a/source/blender/blenkernel/intern/attribute_math.cc
+++ b/source/blender/blenkernel/intern/attribute_math.cc
@@ -18,18 +18,21 @@
namespace blender::attribute_math {
-Color4fMixer::Color4fMixer(MutableSpan<Color4f> output_buffer, Color4f default_color)
+ColorGeometryMixer::ColorGeometryMixer(MutableSpan<ColorGeometry4f> output_buffer,
+ ColorGeometry4f default_color)
: buffer_(output_buffer),
default_color_(default_color),
total_weights_(output_buffer.size(), 0.0f)
{
- buffer_.fill(Color4f(0, 0, 0, 0));
+ buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
-void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float weight)
+void ColorGeometryMixer::mix_in(const int64_t index,
+ const ColorGeometry4f &color,
+ const float weight)
{
BLI_assert(weight >= 0.0f);
- Color4f &output_color = buffer_[index];
+ ColorGeometry4f &output_color = buffer_[index];
output_color.r += color.r * weight;
output_color.g += color.g * weight;
output_color.b += color.b * weight;
@@ -37,11 +40,11 @@ void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float
total_weights_[index] += weight;
}
-void Color4fMixer::finalize()
+void ColorGeometryMixer::finalize()
{
for (const int64_t i : buffer_.index_range()) {
const float weight = total_weights_[i];
- Color4f &output_color = buffer_[i];
+ ColorGeometry4f &output_color = buffer_[i];
if (weight > 0.0f) {
const float weight_inv = 1.0f / weight;
output_color.r *= weight_inv;
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index e8879cdda8f..e84b485c466 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -132,7 +132,7 @@ static void blender_version_init(void)
BLI_snprintf(blender_version_string,
ARRAY_SIZE(blender_version_string),
- "%d.%02d.%d%s",
+ "%d.%01d.%d%s",
BLENDER_VERSION / 100,
BLENDER_VERSION % 100,
BLENDER_VERSION_PATCH,
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index efbf19c7381..54fd3f55c31 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -399,7 +399,10 @@ static void setup_app_data(bContext *C,
BKE_lib_override_library_main_resync(
bmain,
curscene,
- bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene));
+ bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene),
+ reports);
+ /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
+ BKE_lib_override_library_main_operations_create(bmain, true);
}
}
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 13ba1957a32..fdf9cf21b85 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -574,8 +574,8 @@ bool BKE_brush_delete(Main *bmain, Brush *brush)
if (brush->id.tag & LIB_TAG_INDIRECT) {
return false;
}
- if (BKE_library_ID_is_indirectly_used(bmain, brush) && ID_REAL_USERS(brush) <= 1 &&
- ID_EXTRA_USERS(brush) == 0) {
+ if (ID_REAL_USERS(brush) <= 1 && ID_EXTRA_USERS(brush) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, brush)) {
return false;
}
@@ -989,6 +989,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothfac = 0.1f;
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 1;
+ brush->gpencil_settings->dilate_pixels = 1;
brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES;
@@ -1399,13 +1400,11 @@ void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, const bool r
}
/* Set default Draw brush. */
- if (reset || brush_prev == NULL) {
- BKE_paint_brush_set(paint, deft_draw);
+ if ((reset == false) && (brush_prev != NULL)) {
+ BKE_paint_brush_set(paint, brush_prev);
}
else {
- if (brush_prev != NULL) {
- BKE_paint_brush_set(paint, brush_prev);
- }
+ BKE_paint_brush_set(paint, deft_draw);
}
}
diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c
index 790fb128c7c..bc63e423c09 100644
--- a/source/blender/blenkernel/intern/bvhutils.c
+++ b/source/blender/blenkernel/intern/bvhutils.c
@@ -1555,6 +1555,10 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
bvh_cache_type,
bvh_cache_p,
mesh_eval_mutex);
+
+ if (looptri_mask != NULL) {
+ MEM_freeN(looptri_mask);
+ }
}
else {
/* Setup BVHTreeFromMesh */
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index ac23f9012a7..aa51ee0017e 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -694,8 +694,7 @@ Collection *BKE_collection_duplicate(Main *bmain,
const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
if (!is_subprocess) {
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
/* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate
* all expected linked data. */
if (ID_IS_LINKED(collection)) {
@@ -726,8 +725,7 @@ Collection *BKE_collection_duplicate(Main *bmain,
#endif
/* Cleanup. */
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_collection_sync(bmain);
}
@@ -1007,7 +1005,7 @@ Collection *BKE_collection_object_find(Main *bmain,
return NULL;
}
-bool BKE_collection_is_empty(Collection *collection)
+bool BKE_collection_is_empty(const Collection *collection)
{
return BLI_listbase_is_empty(&collection->gobject) &&
BLI_listbase_is_empty(&collection->children);
@@ -1150,6 +1148,8 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
BKE_main_collection_sync(bmain);
}
+ DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
+
return true;
}
@@ -1201,6 +1201,8 @@ bool BKE_collection_object_remove(Main *bmain,
BKE_main_collection_sync(bmain);
}
+ DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
+
return true;
}
@@ -1302,41 +1304,50 @@ static void collection_missing_parents_remove(Collection *collection)
*
* \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards!
*
- * \param collection: may be \a NULL,
+ * \param parent_collection: The collection owning the pointers that were remapped. May be \a NULL,
+ * in which case whole \a bmain database of collections is checked.
+ * \param child_collection: The collection that was remapped to another pointer. May be \a NULL,
* in which case whole \a bmain database of collections is checked.
*/
-void BKE_collections_child_remove_nulls(Main *bmain, Collection *collection)
+void BKE_collections_child_remove_nulls(Main *bmain,
+ Collection *parent_collection,
+ Collection *child_collection)
{
- if (collection == NULL) {
- /* We need to do the checks in two steps when more than one collection may be involved,
- * otherwise we can miss some cases...
- * Also, master collections are not in bmain, so we also need to loop over scenes.
- */
- for (collection = bmain->collections.first; collection != NULL;
- collection = collection->id.next) {
- collection_null_children_remove(collection);
+ if (child_collection == NULL) {
+ if (parent_collection != NULL) {
+ collection_null_children_remove(parent_collection);
}
- for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
- collection_null_children_remove(scene->master_collection);
+ else {
+ /* We need to do the checks in two steps when more than one collection may be involved,
+ * otherwise we can miss some cases...
+ * Also, master collections are not in bmain, so we also need to loop over scenes.
+ */
+ for (child_collection = bmain->collections.first; child_collection != NULL;
+ child_collection = child_collection->id.next) {
+ collection_null_children_remove(child_collection);
+ }
+ for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
+ collection_null_children_remove(scene->master_collection);
+ }
}
- for (collection = bmain->collections.first; collection != NULL;
- collection = collection->id.next) {
- collection_missing_parents_remove(collection);
+ for (child_collection = bmain->collections.first; child_collection != NULL;
+ child_collection = child_collection->id.next) {
+ collection_missing_parents_remove(child_collection);
}
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
collection_missing_parents_remove(scene->master_collection);
}
}
else {
- for (CollectionParent *parent = collection->parents.first, *parent_next; parent;
+ for (CollectionParent *parent = child_collection->parents.first, *parent_next; parent;
parent = parent_next) {
parent_next = parent->next;
collection_null_children_remove(parent->collection);
- if (!collection_find_child(parent->collection, collection)) {
- BLI_freelinkN(&collection->parents, parent);
+ if (!collection_find_child(parent->collection, child_collection)) {
+ BLI_freelinkN(&child_collection->parents, parent);
}
}
}
@@ -1412,7 +1423,8 @@ static bool collection_instance_find_recursive(Collection *collection,
}
LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) {
- if (collection_instance_find_recursive(collection_child->collection, instance_collection)) {
+ if (collection_child->collection != NULL &&
+ collection_instance_find_recursive(collection_child->collection, instance_collection)) {
return true;
}
}
@@ -1631,6 +1643,13 @@ void BKE_collection_parent_relations_rebuild(Collection *collection)
continue;
}
+ /* Can happen when remapping data partially out-of-Main (during advanced ID management
+ * operations like lib-override resync e.g.). */
+ if ((child->collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) {
+ continue;
+ }
+
+ BLI_assert(collection_find_parent(child->collection, collection) == NULL);
CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), __func__);
cparent->collection = collection;
BLI_addtail(&child->collection->parents, cparent);
@@ -1639,10 +1658,19 @@ void BKE_collection_parent_relations_rebuild(Collection *collection)
static void collection_parents_rebuild_recursive(Collection *collection)
{
+ /* A same collection may be child of several others, no need to process it more than once. */
+ if ((collection->tag & COLLECTION_TAG_RELATION_REBUILD) == 0) {
+ return;
+ }
+
BKE_collection_parent_relations_rebuild(collection);
collection->tag &= ~COLLECTION_TAG_RELATION_REBUILD;
for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) {
+ /* See comment above in `BKE_collection_parent_relations_rebuild`. */
+ if ((collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) {
+ continue;
+ }
collection_parents_rebuild_recursive(child->collection);
}
}
@@ -1666,6 +1694,8 @@ void BKE_main_collections_parent_relations_rebuild(Main *bmain)
/* This function can be called from readfile.c, when this pointer is not guaranteed to be NULL.
*/
if (scene->master_collection != NULL) {
+ BLI_assert(BLI_listbase_is_empty(&scene->master_collection->parents));
+ scene->master_collection->tag |= COLLECTION_TAG_RELATION_REBUILD;
collection_parents_rebuild_recursive(scene->master_collection);
}
}
@@ -2015,12 +2045,10 @@ static void scene_collections_array(Scene *scene,
BLI_assert(collection != NULL);
scene_collection_callback(collection, scene_collections_count, r_collections_array_len);
- if (*r_collections_array_len == 0) {
- return;
- }
+ BLI_assert(*r_collections_array_len > 0);
- Collection **array = MEM_mallocN(sizeof(Collection *) * (*r_collections_array_len),
- "CollectionArray");
+ Collection **array = MEM_malloc_arrayN(
+ *r_collections_array_len, sizeof(Collection *), "CollectionArray");
*r_collections_array = array;
scene_collection_callback(collection, scene_collections_build_array, &array);
}
@@ -2035,8 +2063,9 @@ void BKE_scene_collections_iterator_begin(BLI_Iterator *iter, void *data_in)
CollectionsIteratorData *data = MEM_callocN(sizeof(CollectionsIteratorData), __func__);
data->scene = scene;
+
+ BLI_ITERATOR_INIT(iter);
iter->data = data;
- iter->valid = true;
scene_collections_array(scene, (Collection ***)&data->array, &data->tot);
BLI_assert(data->tot != 0);
@@ -2078,16 +2107,22 @@ typedef struct SceneObjectsIteratorData {
BLI_Iterator scene_collection_iter;
} SceneObjectsIteratorData;
-void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
+static void scene_objects_iterator_begin(BLI_Iterator *iter, Scene *scene, GSet *visited_objects)
{
- Scene *scene = data_in;
SceneObjectsIteratorData *data = MEM_callocN(sizeof(SceneObjectsIteratorData), __func__);
+
+ BLI_ITERATOR_INIT(iter);
iter->data = data;
- /* lookup list ot make sure each object is object called once */
- data->visited = BLI_gset_ptr_new(__func__);
+ /* Lookup list to make sure that each object is only processed once. */
+ if (visited_objects != NULL) {
+ data->visited = visited_objects;
+ }
+ else {
+ data->visited = BLI_gset_ptr_new(__func__);
+ }
- /* we wrap the scenecollection iterator here to go over the scene collections */
+ /* We wrap the scenecollection iterator here to go over the scene collections. */
BKE_scene_collections_iterator_begin(&data->scene_collection_iter, scene);
Collection *collection = data->scene_collection_iter.current;
@@ -2096,6 +2131,13 @@ void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
BKE_scene_objects_iterator_next(iter);
}
+void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
+{
+ Scene *scene = data_in;
+
+ scene_objects_iterator_begin(iter, scene, NULL);
+}
+
/**
* Ensures we only get each object once, even when included in several collections.
*/
@@ -2149,9 +2191,36 @@ void BKE_scene_objects_iterator_end(BLI_Iterator *iter)
SceneObjectsIteratorData *data = iter->data;
if (data) {
BKE_scene_collections_iterator_end(&data->scene_collection_iter);
- BLI_gset_free(data->visited, NULL);
+ if (data->visited != NULL) {
+ BLI_gset_free(data->visited, NULL);
+ }
MEM_freeN(data);
}
}
+/**
+ * Generate a new GSet (or extend given `objects_gset` if not NULL) with all objects referenced by
+ * all collections of given `scene`.
+ *
+ * \note: This will include objects without a base currently (because they would belong to excluded
+ * collections only e.g.).
+ */
+GSet *BKE_scene_objects_as_gset(Scene *scene, GSet *objects_gset)
+{
+ BLI_Iterator iter;
+ scene_objects_iterator_begin(&iter, scene, objects_gset);
+ while (iter.valid) {
+ BKE_scene_objects_iterator_next(&iter);
+ }
+
+ /* `return_gset` is either given `objects_gset` (if non-NULL), or the GSet allocated by the
+ * iterator. Either way, we want to get it back, and prevent `BKE_scene_objects_iterator_end`
+ * from freeing it. */
+ GSet *return_gset = ((SceneObjectsIteratorData *)iter.data)->visited;
+ ((SceneObjectsIteratorData *)iter.data)->visited = NULL;
+ BKE_scene_objects_iterator_end(&iter);
+
+ return return_gset;
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index d04a27adec8..826c79c3764 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1463,30 +1463,20 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
* currently for paths to work it needs to go through the bevlist/displist system (ton)
*/
- if (ct->tar->runtime.curve_cache && ct->tar->runtime.curve_cache->path &&
- ct->tar->runtime.curve_cache->path->data) {
+ if (ct->tar->runtime.curve_cache && ct->tar->runtime.curve_cache->anim_path_accum_length) {
float quat[4];
if ((data->followflag & FOLLOWPATH_STATIC) == 0) {
/* animated position along curve depending on time */
- Nurb *nu = cu->nurb.first;
curvetime = cu->ctime - data->offset;
/* ctime is now a proper var setting of Curve which gets set by Animato like any other var
* that's animated, but this will only work if it actually is animated...
*
* we divide the curvetime calculated in the previous step by the length of the path,
- * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. */
+ * to get a time factor. */
curvetime /= cu->pathlen;
- if (nu && nu->flagu & CU_NURB_CYCLIC) {
- /* If the curve is cyclic, enable looping around if the time is
- * outside the bounds 0..1 */
- if ((curvetime < 0.0f) || (curvetime > 1.0f)) {
- curvetime -= floorf(curvetime);
- }
- }
- else {
- /* The curve is not cyclic, so clamp to the begin/end points. */
+ if (cu->flag & CU_PATH_CLAMP) {
CLAMP(curvetime, 0.0f, 1.0f);
}
}
@@ -1495,13 +1485,13 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
curvetime = data->offset_fac;
}
- if (where_on_path(ct->tar,
- curvetime,
- vec,
- dir,
- (data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL,
- &radius,
- NULL)) { /* quat_pt is quat or NULL*/
+ if (BKE_where_on_path(ct->tar,
+ curvetime,
+ vec,
+ dir,
+ (data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL,
+ &radius,
+ NULL)) { /* quat_pt is quat or NULL*/
float totmat[4][4];
unit_m4(totmat);
@@ -1645,10 +1635,28 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN
float eul[3];
float size[3];
+ /* This constraint is based on euler rotation math, which doesn't work well with shear.
+ * The Y axis is chosen as the main one because constraints are most commonly used on bones.
+ * This also allows using the constraint to simply remove shear. */
+ orthogonalize_m4_stable(cob->matrix, 1, false);
+
+ /* Only do the complex processing if some limits are actually enabled. */
+ if (!(data->flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT))) {
+ return;
+ }
+
+ /* Select the Euler rotation order, defaulting to the owner value. */
+ short rot_order = cob->rotOrder;
+
+ if (data->euler_order != CONSTRAINT_EULER_AUTO) {
+ rot_order = data->euler_order;
+ }
+
+ /* Decompose the matrix using the specified order. */
copy_v3_v3(loc, cob->matrix[3]);
mat4_to_size(size, cob->matrix);
- mat4_to_eulO(eul, cob->rotOrder, cob->matrix);
+ mat4_to_eulO(eul, rot_order, cob->matrix);
/* constraint data uses radians internally */
@@ -1681,7 +1689,7 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN
}
}
- loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, cob->rotOrder);
+ loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, rot_order);
}
static bConstraintTypeInfo CTI_ROTLIMIT = {
@@ -2850,7 +2858,7 @@ static void actcon_get_tarmat(struct Depsgraph *depsgraph,
* including rotation order, otherwise this fails. */
pchan = cob->pchan;
- tchan = BKE_pose_channel_verify(&pose, pchan->name);
+ tchan = BKE_pose_channel_ensure(&pose, pchan->name);
tchan->rotmode = pchan->rotmode;
/* evaluate action using workob (it will only set the PoseChannel in question) */
@@ -3784,8 +3792,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
BKE_object_minmax(ct->tar, curveMin, curveMax, true);
/* get targetmatrix */
- if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->path &&
- data->tar->runtime.curve_cache->path->data) {
+ if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->anim_path_accum_length) {
float vec[4], dir[3], totmat[4][4];
float curvetime;
short clamp_axis;
@@ -3869,7 +3876,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
}
/* 3. position on curve */
- if (where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) {
+ if (BKE_where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) {
unit_m4(totmat);
copy_v3_v3(totmat[3], vec);
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index cbf7a4483c0..81830f5bb61 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -80,7 +80,17 @@ struct bContext {
struct ARegion *menu;
struct wmGizmoGroup *gizmo_group;
struct bContextStore *store;
- const char *operator_poll_msg; /* reason for poll failing */
+
+ /* Operator poll. */
+ /**
+ * Store the reason the poll function fails (static string, not allocated).
+ * For more advanced formatting use `operator_poll_msg_dyn_params`.
+ */
+ const char *operator_poll_msg;
+ /**
+ * Store values to dynamically to create the string (called when a tool-tip is shown).
+ */
+ struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params;
} wm;
/* data context */
@@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C)
{
bContext *newC = MEM_dupallocN((void *)C);
+ memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params));
+
return newC;
}
void CTX_free(bContext *C)
{
+ /* This may contain a dynamically allocated message, free. */
+ CTX_wm_operator_poll_msg_clear(C);
+
MEM_freeN(C);
}
@@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup)
C->wm.gizmo_group = gzgroup;
}
+void CTX_wm_operator_poll_msg_clear(bContext *C)
+{
+ struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
+ if (params->free_fn != NULL) {
+ params->free_fn(C, params->user_data);
+ }
+ params->get_fn = NULL;
+ params->free_fn = NULL;
+ params->user_data = NULL;
+
+ C->wm.operator_poll_msg = NULL;
+}
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
{
+ CTX_wm_operator_poll_msg_clear(C);
+
C->wm.operator_poll_msg = msg;
}
-const char *CTX_wm_operator_poll_msg_get(bContext *C)
+void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
+ const struct bContextPollMsgDyn_Params *params)
{
+ CTX_wm_operator_poll_msg_clear(C);
+
+ C->wm.operator_poll_msg_dyn_params = *params;
+}
+
+const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
+{
+ struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
+ if (params->get_fn != NULL) {
+ char *msg = params->get_fn(C, params->user_data);
+ if (msg != NULL) {
+ *r_free = true;
+ }
+ return msg;
+ }
+
+ *r_free = false;
return IFACE_(C->wm.operator_poll_msg);
}
diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc
index bc89fa85cea..1ff0ca92306 100644
--- a/source/blender/blenkernel/intern/cryptomatte.cc
+++ b/source/blender/blenkernel/intern/cryptomatte.cc
@@ -54,7 +54,7 @@ struct CryptomatteSession {
/* Layer names in order of creation. */
blender::Vector<std::string> layer_names;
- CryptomatteSession();
+ CryptomatteSession() = default;
CryptomatteSession(const Main *bmain);
CryptomatteSession(StampData *stamp_data);
CryptomatteSession(const Scene *scene);
@@ -67,10 +67,6 @@ struct CryptomatteSession {
#endif
};
-CryptomatteSession::CryptomatteSession()
-{
-}
-
CryptomatteSession::CryptomatteSession(const Main *bmain)
{
if (!BLI_listbase_is_empty(&bmain->objects)) {
@@ -425,6 +421,9 @@ static bool from_manifest(CryptomatteLayer &layer, blender::StringRefNull manife
}
const int quoted_hash_len = quoted_string_len_(ref);
+ if (quoted_hash_len < 2) {
+ return false;
+ }
const int hash_len = quoted_hash_len - 2;
CryptomatteHash hash = CryptomatteHash::from_hex_encoded(ref.substr(1, hash_len));
ref = ref.drop_prefix(quoted_hash_len);
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index f3551c98f07..65cdb8503a4 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -414,6 +414,7 @@ void BKE_curve_init(Curve *cu, const short curve_type)
cu->tb[0].w = cu->tb[0].h = 0.0;
}
else if (cu->type == OB_SURF) {
+ cu->flag |= CU_3D;
cu->resolv = 4;
}
cu->bevel_profile = NULL;
@@ -462,24 +463,19 @@ short BKE_curve_type_get(const Curve *cu)
return type;
}
-void BKE_curve_curve_dimension_update(Curve *cu)
+void BKE_curve_dimension_update(Curve *cu)
{
ListBase *nurbs = BKE_curve_nurbs_get(cu);
+ bool is_2d = CU_IS_2D(cu);
- if (cu->flag & CU_3D) {
- LISTBASE_FOREACH (Nurb *, nu, nurbs) {
- nu->flag &= ~CU_2D;
+ LISTBASE_FOREACH (Nurb *, nu, nurbs) {
+ if (is_2d) {
+ BKE_nurb_project_2d(nu);
}
- }
- else {
- LISTBASE_FOREACH (Nurb *, nu, nurbs) {
- nu->flag |= CU_2D;
- BKE_nurb_test_2d(nu);
- /* since the handles are moved they need to be auto-located again */
- if (nu->type == CU_BEZIER) {
- BKE_nurb_handles_calc(nu);
- }
+ /* since the handles are moved they need to be auto-located again */
+ if (nu->type == CU_BEZIER) {
+ BKE_nurb_handles_calc(nu);
}
}
}
@@ -489,7 +485,10 @@ void BKE_curve_type_test(Object *ob)
ob->type = BKE_curve_type_get(ob->data);
if (ob->type == OB_CURVE) {
- BKE_curve_curve_dimension_update((Curve *)ob->data);
+ Curve *cu = ob->data;
+ if (CU_IS_2D(cu)) {
+ BKE_curve_dimension_update(cu);
+ }
}
}
@@ -596,11 +595,11 @@ bool BKE_nurbList_index_get_co(ListBase *nurb, const int index, float r_co[3])
return false;
}
-int BKE_nurbList_verts_count(ListBase *nurb)
+int BKE_nurbList_verts_count(const ListBase *nurb)
{
int tot = 0;
- LISTBASE_FOREACH (Nurb *, nu, nurb) {
+ LISTBASE_FOREACH (const Nurb *, nu, nurb) {
if (nu->bezt) {
tot += 3 * nu->pntsu;
}
@@ -612,7 +611,7 @@ int BKE_nurbList_verts_count(ListBase *nurb)
return tot;
}
-int BKE_nurbList_verts_count_without_handles(ListBase *nurb)
+int BKE_nurbList_verts_count_without_handles(const ListBase *nurb)
{
int tot = 0;
@@ -745,16 +744,12 @@ void BKE_nurbList_duplicate(ListBase *lb1, const ListBase *lb2)
}
}
-void BKE_nurb_test_2d(Nurb *nu)
+void BKE_nurb_project_2d(Nurb *nu)
{
BezTriple *bezt;
BPoint *bp;
int a;
- if ((nu->flag & CU_2D) == 0) {
- return;
- }
-
if (nu->type == CU_BEZIER) {
a = nu->pntsu;
bezt = nu->bezt;
@@ -779,7 +774,7 @@ void BKE_nurb_test_2d(Nurb *nu)
* if use_radius is truth, minmax will take points' radius into account,
* which will make boundbox closer to beveled curve.
*/
-void BKE_nurb_minmax(Nurb *nu, bool use_radius, float min[3], float max[3])
+void BKE_nurb_minmax(const Nurb *nu, bool use_radius, float min[3], float max[3])
{
BezTriple *bezt;
BPoint *bp;
@@ -1928,7 +1923,7 @@ static int cu_isectLL(const float v1[3],
return 0;
}
-static bool bevelinside(BevList *bl1, BevList *bl2)
+static bool bevelinside(const BevList *bl1, const BevList *bl2)
{
/* is bl2 INSIDE bl1 ? with left-right method and "lambda's" */
/* returns '1' if correct hole */
@@ -2052,8 +2047,8 @@ static void calc_bevel_sin_cos(
*r_cosa = x3 / t02;
}
-static void tilt_bezpart(BezTriple *prevbezt,
- BezTriple *bezt,
+static void tilt_bezpart(const BezTriple *prevbezt,
+ const BezTriple *bezt,
Nurb *nu,
float *tilt_array,
float *radius_array,
@@ -2061,7 +2056,7 @@ static void tilt_bezpart(BezTriple *prevbezt,
int resolu,
int stride)
{
- BezTriple *pprev, *next, *last;
+ const BezTriple *pprev, *next, *last;
float fac, dfac, t[4];
int a;
@@ -2712,8 +2707,9 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
continue;
}
- /* check if we will calculate tilt data */
- do_tilt = CU_DO_TILT(cu, nu);
+ /* Tilt, as the rotation angle of curve control points, is only calculated for 3D curves,
+ * (since this transformation affects the 3D space). */
+ do_tilt = (cu->flag & CU_3D) != 0;
/* Normal display uses the radius, better just to calculate them. */
do_radius = CU_DO_RADIUS(cu, nu);
@@ -2756,7 +2752,6 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bevp->tilt = bp->tilt;
bevp->radius = bp->radius;
bevp->weight = bp->weight;
- bevp->split_tag = true;
bp++;
if (seglen != NULL && len != 0) {
*seglen = len_v3v3(bevp->vec, bp->vec);
@@ -2828,7 +2823,6 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bevp->tilt = prevbezt->tilt;
bevp->radius = prevbezt->radius;
bevp->weight = prevbezt->weight;
- bevp->split_tag = true;
bevp->dupe_tag = false;
bevp++;
bl->nr++;
@@ -2879,21 +2873,6 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
sizeof(BevPoint));
}
- /* indicate with handlecodes double points */
- if (prevbezt->h1 == prevbezt->h2) {
- if (ELEM(prevbezt->h1, 0, HD_VECT)) {
- bevp->split_tag = true;
- }
- }
- else {
- if (ELEM(prevbezt->h1, 0, HD_VECT)) {
- bevp->split_tag = true;
- }
- else if (ELEM(prevbezt->h2, 0, HD_VECT)) {
- bevp->split_tag = true;
- }
- }
-
/* seglen */
if (seglen != NULL) {
*seglen = 0;
@@ -3142,7 +3121,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
}
/* turning direction */
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
sd = sortdata;
for (a = 0; a < poly; a++, sd++) {
if (sd->bl->hole == sd->dir) {
@@ -3162,7 +3141,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
}
/* STEP 4: 2D-COSINES or 3D ORIENTATION */
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
/* 2D Curves */
LISTBASE_FOREACH (BevList *, bl, bev) {
if (bl->nr < 2) {
@@ -3213,7 +3192,6 @@ static void calchandleNurb_intern(BezTriple *bezt,
float pt[3];
float dvec_a[3], dvec_b[3];
float len, len_a, len_b;
- float len_ratio;
const float eps = 1e-5;
/* assume normal handle until we check */
@@ -3265,8 +3243,6 @@ static void calchandleNurb_intern(BezTriple *bezt,
len_b = 1.0f;
}
- len_ratio = len_a / len_b;
-
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { /* auto */
float tvec[3];
tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
@@ -3399,7 +3375,7 @@ static void calchandleNurb_intern(BezTriple *bezt,
len_b = 1.0f;
}
- len_ratio = len_a / len_b;
+ const float len_ratio = len_a / len_b;
if (bezt->f1 & handle_sel_flag) { /* order of calculation */
if (ELEM(bezt->h2, HD_ALIGN, HD_ALIGN_DOUBLESIDE)) { /* aligned */
@@ -4681,12 +4657,12 @@ void BKE_nurb_direction_switch(Nurb *nu)
}
}
-void BKE_curve_nurbs_vert_coords_get(ListBase *lb, float (*vert_coords)[3], int vert_len)
+void BKE_curve_nurbs_vert_coords_get(const ListBase *lb, float (*vert_coords)[3], int vert_len)
{
float *co = vert_coords[0];
- LISTBASE_FOREACH (Nurb *, nu, lb) {
+ LISTBASE_FOREACH (const Nurb *, nu, lb) {
if (nu->type == CU_BEZIER) {
- BezTriple *bezt = nu->bezt;
+ const BezTriple *bezt = nu->bezt;
for (int i = 0; i < nu->pntsu; i++, bezt++) {
copy_v3_v3(co, bezt->vec[0]);
co += 3;
@@ -4697,7 +4673,7 @@ void BKE_curve_nurbs_vert_coords_get(ListBase *lb, float (*vert_coords)[3], int
}
}
else {
- BPoint *bp = nu->bp;
+ const BPoint *bp = nu->bp;
for (int i = 0; i < nu->pntsu * nu->pntsv; i++, bp++) {
copy_v3_v3(co, bp->vec);
co += 3;
@@ -4708,7 +4684,7 @@ void BKE_curve_nurbs_vert_coords_get(ListBase *lb, float (*vert_coords)[3], int
UNUSED_VARS_NDEBUG(vert_len);
}
-float (*BKE_curve_nurbs_vert_coords_alloc(ListBase *lb, int *r_vert_len))[3]
+float (*BKE_curve_nurbs_vert_coords_alloc(const ListBase *lb, int *r_vert_len))[3]
{
const int vert_len = BKE_nurbList_verts_count(lb);
float(*vert_coords)[3] = MEM_malloc_arrayN(vert_len, sizeof(*vert_coords), __func__);
@@ -4747,9 +4723,7 @@ void BKE_curve_nurbs_vert_coords_apply_with_mat4(ListBase *lb,
}
if (constrain_2d) {
- if (nu->flag & CU_2D) {
- BKE_nurb_test_2d(nu);
- }
+ BKE_nurb_project_2d(nu);
}
calchandlesNurb_intern(nu, SELECT, true);
@@ -4785,24 +4759,22 @@ void BKE_curve_nurbs_vert_coords_apply(ListBase *lb,
}
if (constrain_2d) {
- if (nu->flag & CU_2D) {
- BKE_nurb_test_2d(nu);
- }
+ BKE_nurb_project_2d(nu);
}
calchandlesNurb_intern(nu, SELECT, true);
}
}
-float (*BKE_curve_nurbs_key_vert_coords_alloc(ListBase *lb, float *key, int *r_vert_len))[3]
+float (*BKE_curve_nurbs_key_vert_coords_alloc(const ListBase *lb, float *key, int *r_vert_len))[3]
{
int vert_len = BKE_nurbList_verts_count(lb);
float(*cos)[3] = MEM_malloc_arrayN(vert_len, sizeof(*cos), __func__);
float *co = cos[0];
- LISTBASE_FOREACH (Nurb *, nu, lb) {
+ LISTBASE_FOREACH (const Nurb *, nu, lb) {
if (nu->type == CU_BEZIER) {
- BezTriple *bezt = nu->bezt;
+ const BezTriple *bezt = nu->bezt;
for (int i = 0; i < nu->pntsu; i++, bezt++) {
copy_v3_v3(co, &key[0]);
@@ -4815,7 +4787,7 @@ float (*BKE_curve_nurbs_key_vert_coords_alloc(ListBase *lb, float *key, int *r_v
}
}
else {
- BPoint *bp = nu->bp;
+ const BPoint *bp = nu->bp;
for (int i = 0; i < nu->pntsu * nu->pntsv; i++, bp++) {
copy_v3_v3(co, key);
@@ -5233,7 +5205,7 @@ bool BKE_curve_minmax(Curve *cu, bool use_radius, float min[3], float max[3])
use_radius = false;
}
/* Do bounding box based on splines. */
- LISTBASE_FOREACH (Nurb *, nu, nurb_lb) {
+ LISTBASE_FOREACH (const Nurb *, nu, nurb_lb) {
BKE_nurb_minmax(nu, use_radius, min, max);
}
const bool result = (BLI_listbase_is_empty(nurb_lb) == false);
@@ -5429,12 +5401,12 @@ void BKE_curve_material_index_remove(Curve *cu, int index)
}
}
-bool BKE_curve_material_index_used(Curve *cu, int index)
+bool BKE_curve_material_index_used(const Curve *cu, int index)
{
const int curvetype = BKE_curve_type_get(cu);
if (curvetype == OB_FONT) {
- struct CharInfo *info = cu->strinfo;
+ const struct CharInfo *info = cu->strinfo;
for (int i = cu->len_char32 - 1; i >= 0; i--, info++) {
if (info->mat_nr == index) {
return true;
@@ -5442,7 +5414,7 @@ bool BKE_curve_material_index_used(Curve *cu, int index)
}
}
else {
- LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
+ LISTBASE_FOREACH (const Nurb *, nu, &cu->nurb) {
if (nu->mat_nr == index) {
return true;
}
diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c
index 911a98cb607..7f2cdfa59d3 100644
--- a/source/blender/blenkernel/intern/curve_bevel.c
+++ b/source/blender/blenkernel/intern/curve_bevel.c
@@ -56,7 +56,9 @@ static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve)
return (curve->flag & CU_FRONT) ? FRONT : BACK;
}
-static void bevel_quarter_fill(Curve *curve, float *quarter_coords_x, float *quarter_coords_y)
+static void bevel_quarter_fill(const Curve *curve,
+ float *quarter_coords_x,
+ float *quarter_coords_y)
{
if (curve->bevel_mode == CU_BEV_MODE_ROUND) {
float angle = 0.0f;
@@ -83,7 +85,7 @@ static void bevel_quarter_fill(Curve *curve, float *quarter_coords_x, float *qua
}
}
-static void curve_bevel_make_extrude_and_fill(Curve *cu,
+static void curve_bevel_make_extrude_and_fill(const Curve *cu,
ListBase *disp,
const bool use_extrude,
const CurveBevelFillType fill_type)
@@ -193,7 +195,7 @@ static void curve_bevel_make_extrude_and_fill(Curve *cu,
}
}
-static void curve_bevel_make_full_circle(Curve *cu, ListBase *disp)
+static void curve_bevel_make_full_circle(const Curve *cu, ListBase *disp)
{
const int nr = 4 + 2 * cu->bevresol;
@@ -218,7 +220,7 @@ static void curve_bevel_make_full_circle(Curve *cu, ListBase *disp)
}
}
-static void curve_bevel_make_only_extrude(Curve *cu, ListBase *disp)
+static void curve_bevel_make_only_extrude(const Curve *cu, ListBase *disp)
{
DispList *dl = MEM_callocN(sizeof(DispList), __func__);
dl->verts = MEM_malloc_arrayN(2, sizeof(float[3]), __func__);
@@ -235,7 +237,7 @@ static void curve_bevel_make_only_extrude(Curve *cu, ListBase *disp)
fp[5] = cu->ext1;
}
-static void curve_bevel_make_from_object(Curve *cu, ListBase *disp)
+static void curve_bevel_make_from_object(const Curve *cu, ListBase *disp)
{
if (cu->bevobj == NULL) {
return;
@@ -287,15 +289,13 @@ static void curve_bevel_make_from_object(Curve *cu, ListBase *disp)
}
}
-void BKE_curve_bevel_make(Object *ob, ListBase *disp)
+ListBase BKE_curve_bevel_make(const Curve *curve)
{
- Curve *curve = ob->data;
-
- BLI_listbase_clear(disp);
+ ListBase bevel_shape = {NULL, NULL};
if (curve->bevel_mode == CU_BEV_MODE_OBJECT) {
if (curve->bevobj != NULL) {
- curve_bevel_make_from_object(curve, disp);
+ curve_bevel_make_from_object(curve, &bevel_shape);
}
}
else {
@@ -303,18 +303,20 @@ void BKE_curve_bevel_make(Object *ob, ListBase *disp)
const bool use_bevel = curve->ext2 != 0.0f;
/* Pass. */
if (use_extrude && !use_bevel) {
- curve_bevel_make_only_extrude(curve, disp);
+ curve_bevel_make_only_extrude(curve, &bevel_shape);
}
else if (use_extrude || use_bevel) {
CurveBevelFillType fill_type = curve_bevel_get_fill_type(curve);
if (!use_extrude && fill_type == FULL && curve->bevel_mode == CU_BEV_MODE_ROUND) {
- curve_bevel_make_full_circle(curve, disp);
+ curve_bevel_make_full_circle(curve, &bevel_shape);
}
else {
/* The general case for nonzero extrusion or an incomplete loop. */
- curve_bevel_make_extrude_and_fill(curve, disp, use_extrude, fill_type);
+ curve_bevel_make_extrude_and_fill(curve, &bevel_shape, use_extrude, fill_type);
}
}
}
+
+ return bevel_shape;
}
diff --git a/source/blender/blenkernel/intern/curve_convert.c b/source/blender/blenkernel/intern/curve_convert.c
index 988ddb3bbc0..5bcce9c339e 100644
--- a/source/blender/blenkernel/intern/curve_convert.c
+++ b/source/blender/blenkernel/intern/curve_convert.c
@@ -44,7 +44,7 @@ static Curve *curve_from_font_object(Object *object, Depsgraph *depsgraph)
new_curve->type = OB_CURVE;
new_curve->flag &= ~CU_3D;
- BKE_curve_curve_dimension_update(new_curve);
+ BKE_curve_dimension_update(new_curve);
return new_curve;
}
diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c
index 63da7c1dd11..10c6d2213ff 100644
--- a/source/blender/blenkernel/intern/curve_deform.c
+++ b/source/blender/blenkernel/intern/curve_deform.c
@@ -68,75 +68,7 @@ static void init_curve_deform(const Object *ob_curve, const Object *ob_target, C
}
/**
- * This makes sure we can extend for non-cyclic.
- *
- * \return Success.
- */
-static bool where_on_path_deform(const Object *ob_curve,
- float ctime,
- float r_vec[4],
- float r_dir[3],
- float r_quat[4],
- float *r_radius)
-{
- BevList *bl;
- float ctime1;
- int cycl = 0;
-
- /* test for cyclic */
- bl = ob_curve->runtime.curve_cache->bev.first;
- if (!bl->nr) {
- return false;
- }
- if (bl->poly > -1) {
- cycl = 1;
- }
-
- if (cycl == 0) {
- ctime1 = CLAMPIS(ctime, 0.0f, 1.0f);
- }
- else {
- ctime1 = ctime;
- }
-
- /* vec needs 4 items */
- if (where_on_path(ob_curve, ctime1, r_vec, r_dir, r_quat, r_radius, NULL)) {
-
- if (cycl == 0) {
- Path *path = ob_curve->runtime.curve_cache->path;
- float dvec[3];
-
- if (ctime < 0.0f) {
- sub_v3_v3v3(dvec, path->data[1].vec, path->data[0].vec);
- mul_v3_fl(dvec, ctime * (float)path->len);
- add_v3_v3(r_vec, dvec);
- if (r_quat) {
- copy_qt_qt(r_quat, path->data[0].quat);
- }
- if (r_radius) {
- *r_radius = path->data[0].radius;
- }
- }
- else if (ctime > 1.0f) {
- sub_v3_v3v3(dvec, path->data[path->len - 1].vec, path->data[path->len - 2].vec);
- mul_v3_fl(dvec, (ctime - 1.0f) * (float)path->len);
- add_v3_v3(r_vec, dvec);
- if (r_quat) {
- copy_qt_qt(r_quat, path->data[path->len - 1].quat);
- }
- if (r_radius) {
- *r_radius = path->data[path->len - 1].radius;
- }
- /* weight - not used but could be added */
- }
- }
- return true;
- }
- return false;
-}
-
-/**
- * For each point, rotate & translate to curve use path, since it has constant distances.
+ * For each point, rotate & translate to curve.
*
* \param co: local coord, result local too.
* \param r_quat: returns quaternion for rotation,
@@ -155,7 +87,7 @@ static bool calc_curve_deform(
return false;
}
- if (ob_curve->runtime.curve_cache->path == NULL) {
+ if (ob_curve->runtime.curve_cache->anim_path_accum_length == NULL) {
return false; /* happens on append, cyclic dependencies and empty curves */
}
@@ -172,8 +104,10 @@ static bool calc_curve_deform(
}
}
else {
- if (LIKELY(ob_curve->runtime.curve_cache->path->totdist > FLT_EPSILON)) {
- fac = -(co[index] - cd->dmax[index]) / (ob_curve->runtime.curve_cache->path->totdist);
+ CurveCache *cc = ob_curve->runtime.curve_cache;
+ float totdist = BKE_anim_path_get_length(cc);
+ if (LIKELY(totdist > FLT_EPSILON)) {
+ fac = -(co[index] - cd->dmax[index]) / totdist;
}
else {
fac = 0.0f;
@@ -192,8 +126,10 @@ static bool calc_curve_deform(
}
}
else {
- if (LIKELY(ob_curve->runtime.curve_cache->path->totdist > FLT_EPSILON)) {
- fac = +(co[index] - cd->dmin[index]) / (ob_curve->runtime.curve_cache->path->totdist);
+ CurveCache *cc = ob_curve->runtime.curve_cache;
+ float totdist = BKE_anim_path_get_length(cc);
+ if (LIKELY(totdist > FLT_EPSILON)) {
+ fac = +(co[index] - cd->dmin[index]) / totdist;
}
else {
fac = 0.0f;
@@ -201,7 +137,7 @@ static bool calc_curve_deform(
}
}
- if (where_on_path_deform(ob_curve, fac, loc, dir, new_quat, &radius)) { /* returns OK */
+ if (BKE_where_on_path(ob_curve, fac, loc, dir, new_quat, &radius, NULL)) { /* returns OK */
float quat[4], cent[3];
if (cd->no_rot_axis) { /* set by caller */
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
new file mode 100644
index 00000000000..9cafe1124b1
--- /dev/null
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -0,0 +1,272 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_listbase.h"
+#include "BLI_map.hh"
+#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+
+#include "DNA_curve_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::float4x4;
+using blender::Map;
+using blender::Span;
+using blender::StringRefNull;
+
+blender::Span<SplinePtr> CurveEval::splines() const
+{
+ return splines_;
+}
+
+blender::MutableSpan<SplinePtr> CurveEval::splines()
+{
+ return splines_;
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all splines.
+ */
+void CurveEval::add_spline(SplinePtr spline)
+{
+ splines_.append(std::move(spline));
+}
+
+void CurveEval::remove_splines(blender::IndexMask mask)
+{
+ for (int i = mask.size() - 1; i >= 0; i--) {
+ splines_.remove_and_reorder(mask.indices()[i]);
+ }
+}
+
+void CurveEval::translate(const float3 &translation)
+{
+ for (SplinePtr &spline : this->splines()) {
+ spline->translate(translation);
+ spline->mark_cache_invalid();
+ }
+}
+
+void CurveEval::transform(const float4x4 &matrix)
+{
+ for (SplinePtr &spline : this->splines()) {
+ spline->transform(matrix);
+ }
+}
+
+void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
+{
+ for (const SplinePtr &spline : this->splines()) {
+ spline->bounds_min_max(min, max, use_evaluated);
+ }
+}
+
+/**
+ * Return the start indices for each of the curve spline's evaluated points, as if they were part
+ * of a flattened array. This can be used to facilitate parallelism by avoiding the need to
+ * accumulate an offset while doing more complex calculations.
+ *
+ * \note The result array is one longer than the spline count; the last element is the total size.
+ */
+blender::Array<int> CurveEval::control_point_offsets() const
+{
+ Array<int> offsets(splines_.size() + 1);
+ int offset = 0;
+ for (const int i : splines_.index_range()) {
+ offsets[i] = offset;
+ offset += splines_[i]->size();
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+/**
+ * Exactly like #control_point_offsets, but uses the number of evaluated points instead.
+ */
+blender::Array<int> CurveEval::evaluated_point_offsets() const
+{
+ Array<int> offsets(splines_.size() + 1);
+ int offset = 0;
+ for (const int i : splines_.index_range()) {
+ offsets[i] = offset;
+ offset += splines_[i]->evaluated_points_size();
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
+{
+ switch (dna_handle_type) {
+ case HD_FREE:
+ return BezierSpline::HandleType::Free;
+ case HD_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case HD_VECT:
+ return BezierSpline::HandleType::Vector;
+ case HD_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case HD_AUTO_ANIM:
+ return BezierSpline::HandleType::Auto;
+ case HD_ALIGN_DOUBLESIDE:
+ return BezierSpline::HandleType::Align;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
+{
+ switch (twist_mode) {
+ case CU_TWIST_Z_UP:
+ return Spline::NormalCalculationMode::ZUp;
+ case CU_TWIST_MINIMUM:
+ return Spline::NormalCalculationMode::Minimum;
+ case CU_TWIST_TANGENT:
+ return Spline::NormalCalculationMode::Tangent;
+ }
+ BLI_assert_unreachable();
+ return Spline::NormalCalculationMode::Minimum;
+}
+
+static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag)
+{
+ switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
+ case CU_NURB_ENDPOINT:
+ return NURBSpline::KnotsMode::EndPoint;
+ case CU_NURB_BEZIER:
+ return NURBSpline::KnotsMode::Bezier;
+ default:
+ return NURBSpline::KnotsMode::Normal;
+ }
+
+ BLI_assert_unreachable();
+ return NURBSpline::KnotsMode::Normal;
+}
+
+std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+
+ const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve));
+
+ /* TODO: Optimize by reserving the correct points size. */
+ LISTBASE_FOREACH (const Nurb *, nurb, nurbs) {
+ switch (nurb->type) {
+ case CU_BEZIER: {
+ std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
+ spline->set_resolution(nurb->resolu);
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+
+ for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) {
+ spline->add_point(bezt.vec[1],
+ handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1),
+ bezt.vec[0],
+ handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2),
+ bezt.vec[2],
+ bezt.radius,
+ bezt.tilt);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ case CU_NURBS: {
+ std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
+ spline->set_resolution(nurb->resolu);
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+ spline->set_order(nurb->orderu);
+ spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu);
+
+ for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
+ spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ case CU_POLY: {
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+
+ for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
+ spline->add_point(bp.vec, bp.radius, bp.tilt);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ }
+
+ /* Though the curve has no attributes, this is necessary to properly set the custom data size. */
+ curve->attributes.reallocate(curve->splines().size());
+
+ /* Note: Normal mode is stored separately in each spline to facilitate combining splines
+ * from multiple curve objects, where the value may be different. */
+ const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
+ dna_curve.twist_mode);
+ for (SplinePtr &spline : curve->splines()) {
+ spline->normal_mode = normal_mode;
+ }
+
+ return curve;
+}
+
+/**
+ * Check the invariants that curve control point attributes should always uphold, necessary
+ * because attributes are stored on splines rather than in a flat array on the curve:
+ * - The same set of attributes exists on every spline.
+ * - Attributes with the same name have the same type on every spline.
+ */
+void CurveEval::assert_valid_point_attributes() const
+{
+#ifdef DEBUG
+ if (splines_.size() == 0) {
+ return;
+ }
+ const int layer_len = splines_.first()->attributes.data.totlayer;
+ Map<StringRefNull, AttributeMetaData> map;
+ for (const SplinePtr &spline : splines_) {
+ BLI_assert(spline->attributes.data.totlayer == layer_len);
+ spline->attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ map.add_or_modify(
+ name,
+ [&](AttributeMetaData *map_data) {
+ /* All unique attribute names should be added on the first spline. */
+ BLI_assert(spline == splines_.first());
+ *map_data = meta_data;
+ },
+ [&](AttributeMetaData *map_data) {
+ /* Attributes on different splines should all have the same type. */
+ BLI_assert(meta_data == *map_data);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ }
+#endif
+} \ No newline at end of file
diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c
index 752e0d4dfcf..00cdc7b3031 100644
--- a/source/blender/blenkernel/intern/curveprofile.c
+++ b/source/blender/blenkernel/intern/curveprofile.c
@@ -1015,7 +1015,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
{
const float total_length = BKE_curveprofile_total_length(profile);
const float segment_length = total_length / n_segments;
- float length_travelled = 0.0f;
float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0);
float distance_to_previous_table_point = 0.0f;
int i_table = 0;
@@ -1029,7 +1028,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
for (int i = 1; i < n_segments; i++) {
/* Travel over all of the points that fit inside this segment. */
while (distance_to_next_table_point < segment_left) {
- length_travelled += distance_to_next_table_point;
segment_left -= distance_to_next_table_point;
i_table++;
distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table);
@@ -1057,7 +1055,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
/* We sampled in between this table point and the next, so the next travel step is smaller. */
distance_to_next_table_point -= segment_left;
distance_to_previous_table_point += segment_left;
- length_travelled += segment_left;
segment_left = segment_length;
}
}
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index d8d9675b42b..710dfdaf137 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -1226,8 +1226,6 @@ static void layerInterp_mvert_skin(const void **sources,
int count,
void *dest)
{
- MVertSkin *vs_dst = dest;
-
float radius[3];
zero_v3(radius);
@@ -1239,7 +1237,7 @@ static void layerInterp_mvert_skin(const void **sources,
}
/* Delay writing to the destination in case dest is in sources. */
- vs_dst = dest;
+ MVertSkin *vs_dst = dest;
copy_v3_v3(vs_dst->radius, radius);
vs_dst->flag &= ~MVERT_SKIN_ROOT;
}
diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c
index 470c2f2d246..314d5f4ff82 100644
--- a/source/blender/blenkernel/intern/customdata_file.c
+++ b/source/blender/blenkernel/intern/customdata_file.c
@@ -24,13 +24,13 @@
#include "MEM_guardedalloc.h"
+#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_fileops.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_customdata_file.h"
-#include "BKE_global.h"
/************************* File Format Definitions ***************************/
@@ -167,7 +167,7 @@ static bool cdf_read_header(CDataFile *cdf)
offset += header->structbytes;
header->structbytes = sizeof(CDataFileHeader);
- if (fseek(f, offset, SEEK_SET) != 0) {
+ if (BLI_fseek(f, offset, SEEK_SET) != 0) {
return false;
}
@@ -201,7 +201,7 @@ static bool cdf_read_header(CDataFile *cdf)
mesh->structbytes = sizeof(CDataFileMeshHeader);
}
- if (fseek(f, offset, SEEK_SET) != 0) {
+ if (BLI_fseek(f, offset, SEEK_SET) != 0) {
return false;
}
@@ -233,7 +233,7 @@ static bool cdf_read_header(CDataFile *cdf)
offset += layer->structbytes;
layer->structbytes = sizeof(CDataFileLayer);
- if (fseek(f, offset, SEEK_SET) != 0) {
+ if (BLI_fseek(f, offset, SEEK_SET) != 0) {
return false;
}
}
@@ -321,7 +321,7 @@ bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay)
offset += cdf->layer[a].datasize;
}
- return (fseek(cdf->readf, offset, SEEK_SET) == 0);
+ return (BLI_fseek(cdf->readf, offset, SEEK_SET) == 0);
}
bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data)
diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c
index 7832f3cd882..e6ef569d4b9 100644
--- a/source/blender/blenkernel/intern/deform.c
+++ b/source/blender/blenkernel/intern/deform.c
@@ -1130,11 +1130,13 @@ static void vgroups_datatransfer_interp(const CustomDataTransferLayerMap *laymap
MDeformWeight *dw_dst = BKE_defvert_find_index(data_dst, idx_dst);
float weight_src = 0.0f, weight_dst = 0.0f;
+ bool has_dw_sources = false;
if (sources) {
for (i = count; i--;) {
for (j = data_src[i]->totweight; j--;) {
if ((dw_src = &data_src[i]->dw[j])->def_nr == idx_src) {
weight_src += dw_src->weight * weights[i];
+ has_dw_sources = true;
break;
}
}
@@ -1152,7 +1154,14 @@ static void vgroups_datatransfer_interp(const CustomDataTransferLayerMap *laymap
CLAMP(weight_src, 0.0f, 1.0f);
- if (!dw_dst) {
+ /* Do not create a destination MDeformWeight data if we had no sources at all. */
+ if (!has_dw_sources) {
+ BLI_assert(weight_src == 0.0f);
+ if (dw_dst) {
+ dw_dst->weight = weight_src;
+ }
+ }
+ else if (!dw_dst) {
BKE_defvert_add_index_notest(data_dst, idx_dst, weight_src);
}
else {
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.cc
index d4581991566..70355f3883d 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -21,9 +21,9 @@
* \ingroup bke
*/
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -34,6 +34,7 @@
#include "DNA_vfont_types.h"
#include "BLI_bitmap.h"
+#include "BLI_index_range.hh"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -60,9 +61,11 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+using blender::IndexRange;
+
static void boundbox_displist_object(Object *ob);
-void BKE_displist_elem_free(DispList *dl)
+static void displist_elem_free(DispList *dl)
{
if (dl) {
if (dl->verts) {
@@ -74,9 +77,6 @@ void BKE_displist_elem_free(DispList *dl)
if (dl->index) {
MEM_freeN(dl->index);
}
- if (dl->bevel_split) {
- MEM_freeN(dl->bevel_split);
- }
MEM_freeN(dl);
}
}
@@ -85,26 +85,11 @@ void BKE_displist_free(ListBase *lb)
{
DispList *dl;
- while ((dl = BLI_pophead(lb))) {
- BKE_displist_elem_free(dl);
+ while ((dl = (DispList *)BLI_pophead(lb))) {
+ displist_elem_free(dl);
}
}
-DispList *BKE_displist_find_or_create(ListBase *lb, int type)
-{
- LISTBASE_FOREACH (DispList *, dl, lb) {
- if (dl->type == type) {
- return dl;
- }
- }
-
- DispList *dl = MEM_callocN(sizeof(DispList), "find_disp");
- dl->type = type;
- BLI_addtail(lb, dl);
-
- return dl;
-}
-
DispList *BKE_displist_find(ListBase *lb, int type)
{
LISTBASE_FOREACH (DispList *, dl, lb) {
@@ -113,12 +98,12 @@ DispList *BKE_displist_find(ListBase *lb, int type)
}
}
- return NULL;
+ return nullptr;
}
-bool BKE_displist_has_faces(ListBase *lb)
+bool BKE_displist_has_faces(const ListBase *lb)
{
- LISTBASE_FOREACH (DispList *, dl, lb) {
+ LISTBASE_FOREACH (const DispList *, dl, lb) {
if (ELEM(dl->type, DL_INDEX3, DL_INDEX4, DL_SURF)) {
return true;
}
@@ -127,20 +112,16 @@ bool BKE_displist_has_faces(ListBase *lb)
return false;
}
-void BKE_displist_copy(ListBase *lbn, ListBase *lb)
+void BKE_displist_copy(ListBase *lbn, const ListBase *lb)
{
BKE_displist_free(lbn);
LISTBASE_FOREACH (const DispList *, dl, lb) {
- DispList *dln = MEM_dupallocN(dl);
+ DispList *dln = (DispList *)MEM_dupallocN(dl);
BLI_addtail(lbn, dln);
- dln->verts = MEM_dupallocN(dl->verts);
- dln->nors = MEM_dupallocN(dl->nors);
- dln->index = MEM_dupallocN(dl->index);
-
- if (dl->bevel_split) {
- dln->bevel_split = MEM_dupallocN(dl->bevel_split);
- }
+ dln->verts = (float *)MEM_dupallocN(dl->verts);
+ dln->nors = (float *)MEM_dupallocN(dl->nors);
+ dln->index = (int *)MEM_dupallocN(dl->index);
}
}
@@ -153,8 +134,8 @@ void BKE_displist_normals_add(ListBase *lb)
LISTBASE_FOREACH (DispList *, dl, lb) {
if (dl->type == DL_INDEX3) {
- if (dl->nors == NULL) {
- dl->nors = MEM_callocN(sizeof(float[3]), "dlnors");
+ if (dl->nors == nullptr) {
+ dl->nors = (float *)MEM_callocN(sizeof(float[3]), "dlnors");
if (dl->flag & DL_BACK_CURVE) {
dl->nors[2] = -1.0f;
@@ -165,8 +146,8 @@ void BKE_displist_normals_add(ListBase *lb)
}
}
else if (dl->type == DL_SURF) {
- if (dl->nors == NULL) {
- dl->nors = MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
+ if (dl->nors == nullptr) {
+ dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
vdata = dl->verts;
ndata = dl->nors;
@@ -215,9 +196,9 @@ void BKE_displist_normals_add(ListBase *lb)
}
}
-void BKE_displist_count(ListBase *lb, int *totvert, int *totface, int *tottri)
+void BKE_displist_count(const ListBase *lb, int *totvert, int *totface, int *tottri)
{
- LISTBASE_FOREACH (DispList *, dl, lb) {
+ LISTBASE_FOREACH (const DispList *, dl, lb) {
int vert_tot = 0;
int face_tot = 0;
int tri_tot = 0;
@@ -258,7 +239,8 @@ void BKE_displist_count(ListBase *lb, int *totvert, int *totface, int *tottri)
}
}
-bool BKE_displist_surfindex_get(DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4)
+bool BKE_displist_surfindex_get(
+ const DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4)
{
if ((dl->flag & DL_CYCL_V) == 0 && a == (dl->parts) - 1) {
return false;
@@ -344,9 +326,9 @@ static void curve_to_displist(const Curve *cu,
* and resolution > 1. */
const bool use_cyclic_sample = is_cyclic && (samples_len != 2);
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
/* Add one to the length because of 'BKE_curve_forward_diff_bezier'. */
- dl->verts = MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = samples_len;
@@ -399,8 +381,8 @@ static void curve_to_displist(const Curve *cu,
}
else if (nu->type == CU_NURBS) {
const int len = (resolution * SEGMENTSU(nu));
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -408,12 +390,12 @@ static void curve_to_displist(const Curve *cu,
dl->charidx = nu->charidx;
dl->type = is_cyclic ? DL_POLY : DL_SEGM;
- BKE_nurb_makeCurve(nu, dl->verts, NULL, NULL, NULL, resolution, sizeof(float[3]));
+ BKE_nurb_makeCurve(nu, dl->verts, nullptr, nullptr, nullptr, resolution, sizeof(float[3]));
}
else if (nu->type == CU_POLY) {
const int len = nu->pntsu;
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -441,7 +423,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const float normal_proj[3],
const bool flip_normal)
{
- if (dispbase == NULL) {
+ if (dispbase == nullptr) {
return;
}
if (BLI_listbase_is_empty(dispbase)) {
@@ -477,14 +459,14 @@ void BKE_displist_fill(const ListBase *dispbase,
sf_ctx.poly_nr++;
/* Make verts and edges. */
- ScanFillVert *sf_vert = NULL;
- ScanFillVert *sf_vert_last = NULL;
- ScanFillVert *sf_vert_new = NULL;
+ ScanFillVert *sf_vert = nullptr;
+ ScanFillVert *sf_vert_last = nullptr;
+ ScanFillVert *sf_vert_new = nullptr;
for (int i = 0; i < dl->nr; i++) {
sf_vert_last = sf_vert;
sf_vert = BLI_scanfill_vert_add(&sf_ctx, &dl->verts[3 * i]);
totvert++;
- if (sf_vert_last == NULL) {
+ if (sf_vert_last == nullptr) {
sf_vert_new = sf_vert;
}
else {
@@ -492,7 +474,7 @@ void BKE_displist_fill(const ListBase *dispbase,
}
}
- if (sf_vert != NULL && sf_vert_new != NULL) {
+ if (sf_vert != nullptr && sf_vert_new != nullptr) {
BLI_scanfill_edge_add(&sf_ctx, sf_vert, sf_vert_new);
}
}
@@ -509,7 +491,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const int triangles_len = BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal_proj);
if (totvert != 0 && triangles_len != 0) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), "filldisplist");
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), "filldisplist");
dlnew->type = DL_INDEX3;
dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE));
dlnew->rt = (dl_rt_accum & CU_SMOOTH);
@@ -517,8 +499,8 @@ void BKE_displist_fill(const ListBase *dispbase,
dlnew->nr = totvert;
dlnew->parts = triangles_len;
- dlnew->index = MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
+ dlnew->index = (int *)MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
/* vert data */
int i;
@@ -557,16 +539,16 @@ void BKE_displist_fill(const ListBase *dispbase,
static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
{
- ListBase front = {NULL, NULL};
- ListBase back = {NULL, NULL};
+ ListBase front = {nullptr, nullptr};
+ ListBase back = {nullptr, nullptr};
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
if (dl->type == DL_SURF) {
if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U) == 0) {
if ((cu->flag & CU_BACK) && (dl->flag & DL_BACK_CURVE)) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&front, dlnew);
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -583,9 +565,9 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
}
}
if ((cu->flag & CU_FRONT) && (dl->flag & DL_FRONT_CURVE)) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&back, dlnew);
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -615,7 +597,7 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
BKE_displist_fill(dispbase, dispbase, z_up, false);
}
-static void curve_to_filledpoly(Curve *cu, ListBase *UNUSED(nurb), ListBase *dispbase)
+static void curve_to_filledpoly(const Curve *cu, ListBase *dispbase)
{
if (!CU_DO_2DFILL(cu)) {
return;
@@ -635,18 +617,21 @@ static void curve_to_filledpoly(Curve *cu, ListBase *UNUSED(nurb), ListBase *dis
* - first point left, last point right
* - based on subdivided points in original curve, not on points in taper curve (still)
*/
-static float displist_calc_taper(Depsgraph *depsgraph, Scene *scene, Object *taperobj, float fac)
+static float displist_calc_taper(Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *taperobj,
+ float fac)
{
- DispList *dl;
-
- if (taperobj == NULL || taperobj->type != OB_CURVE) {
+ if (taperobj == nullptr || taperobj->type != OB_CURVE) {
return 1.0;
}
- dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL;
- if (dl == NULL) {
+ DispList *dl = taperobj->runtime.curve_cache ?
+ (DispList *)taperobj->runtime.curve_cache->disp.first :
+ nullptr;
+ if (dl == nullptr) {
BKE_displist_make_curveTypes(depsgraph, scene, taperobj, false, false);
- dl = taperobj->runtime.curve_cache->disp.first;
+ dl = (DispList *)taperobj->runtime.curve_cache->disp.first;
}
if (dl) {
float minx, dx, *fp;
@@ -678,9 +663,9 @@ static float displist_calc_taper(Depsgraph *depsgraph, Scene *scene, Object *tap
}
float BKE_displist_calc_taper(
- Depsgraph *depsgraph, Scene *scene, Object *taperobj, int cur, int tot)
+ Depsgraph *depsgraph, const Scene *scene, Object *taperobj, int cur, int tot)
{
- float fac = ((float)cur) / (float)(tot - 1);
+ const float fac = ((float)cur) / (float)(tot - 1);
return displist_calc_taper(depsgraph, scene, taperobj, fac);
}
@@ -696,7 +681,8 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
BKE_displist_free(&(ob->runtime.curve_cache->disp));
}
else {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall");
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for MBall");
}
BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp);
@@ -704,7 +690,7 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
object_deform_mball(ob, &ob->runtime.curve_cache->disp);
- /* NOP for MBALLs anyway... */
+ /* No-op for MBALLs anyway... */
boundbox_displist_object(ob);
}
}
@@ -720,30 +706,22 @@ void BKE_displist_make_mball_forRender(Depsgraph *depsgraph,
object_deform_mball(ob, dispbase);
}
-static ModifierData *curve_get_tessellate_point(Scene *scene,
- Object *ob,
+static ModifierData *curve_get_tessellate_point(const Scene *scene,
+ const Object *ob,
const bool for_render,
const bool editmode)
{
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- ModifierData *pretessellatePoint;
- int required_mode;
-
- if (for_render) {
- required_mode = eModifierMode_Render;
- }
- else {
- required_mode = eModifierMode_Realtime;
- }
+ ModifierMode required_mode = for_render ? eModifierMode_Render : eModifierMode_Realtime;
if (editmode) {
- required_mode |= eModifierMode_Editmode;
+ required_mode = (ModifierMode)((int)required_mode | eModifierMode_Editmode);
}
- pretessellatePoint = NULL;
+ ModifierData *pretessellatePoint = nullptr;
for (; md; md = md->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;
@@ -757,8 +735,7 @@ static ModifierData *curve_get_tessellate_point(Scene *scene,
/* this modifiers are moving point of tessellation automatically
* (some of them even can't be applied on tessellated curve), set flag
- * for information button in modifier's header
- */
+ * for information button in modifier's header. */
md->mode |= eModifierMode_ApplyOnSpline;
}
else if (md->mode & eModifierMode_ApplyOnSpline) {
@@ -769,48 +746,39 @@ static ModifierData *curve_get_tessellate_point(Scene *scene,
return pretessellatePoint;
}
-/* Return true if any modifier was applied. */
+/**
+ * \return True if any modifier was applied.
+ */
bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
- Scene *scene,
+ const Scene *scene,
Object *ob,
ListBase *source_nurb,
ListBase *target_nurb,
const bool for_render)
{
- VirtualModifierData virtualModifierData;
- ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- ModifierData *pretessellatePoint;
- Curve *cu = ob->data;
- int numElems = 0, numVerts = 0;
- const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- ModifierApplyFlag apply_flag = 0;
- float(*deformedVerts)[3] = NULL;
- float *keyVerts = NULL;
- int required_mode;
- bool modified = false;
+ const Curve *cu = (const Curve *)ob->data;
BKE_modifiers_clear_errors(ob);
+ const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
+ ModifierMode required_mode = for_render ? eModifierMode_Render : eModifierMode_Realtime;
if (editmode) {
- apply_flag |= MOD_APPLY_USECACHE;
+ required_mode = (ModifierMode)((int)required_mode | eModifierMode_Editmode);
}
- if (for_render) {
- apply_flag |= MOD_APPLY_RENDER;
- required_mode = eModifierMode_Render;
- }
- else {
- required_mode = eModifierMode_Realtime;
- }
-
- const ModifierEvalContext mectx = {depsgraph, ob, apply_flag};
-
- pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
+ ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
if (editmode) {
- required_mode |= eModifierMode_Editmode;
+ apply_flag = MOD_APPLY_USECACHE;
+ }
+ if (for_render) {
+ apply_flag = MOD_APPLY_RENDER;
}
+ float *keyVerts = nullptr;
+ float(*deformedVerts)[3] = nullptr;
+ int numVerts = 0;
if (!editmode) {
+ int numElems = 0;
keyVerts = BKE_key_evaluate_object(ob, &numElems);
if (keyVerts) {
@@ -824,9 +792,15 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
}
}
+ const ModifierEvalContext mectx = {depsgraph, ob, apply_flag};
+ ModifierData *pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
+ bool modified = false;
+
if (pretessellatePoint) {
- for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ VirtualModifierData virtualModifierData;
+ for (ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); md;
+ md = md->next) {
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -839,7 +813,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts);
}
- mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts);
+ mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts);
modified = true;
if (md == pretessellatePoint) {
@@ -864,18 +838,16 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[3]
{
- float(*allverts)[3], *fp;
-
*r_vert_len = 0;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
*r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr;
}
- allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts");
- fp = (float *)allverts;
+ float(*allverts)[3] = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__);
+ float *fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
- int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
+ const int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
memcpy(fp, dl->verts, sizeof(float) * ofs);
fp += ofs;
}
@@ -883,11 +855,9 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[
return allverts;
}
-static void displist_vert_coords_apply(ListBase *dispbase, float (*allverts)[3])
+static void displist_vert_coords_apply(ListBase *dispbase, const float (*allverts)[3])
{
- const float *fp;
-
- fp = (float *)allverts;
+ const float *fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
memcpy(dl->verts, fp, sizeof(float) * ofs);
@@ -896,70 +866,62 @@ static void displist_vert_coords_apply(ListBase *dispbase, float (*allverts)[3])
}
static void curve_calc_modifiers_post(Depsgraph *depsgraph,
- Scene *scene,
+ const Scene *scene,
Object *ob,
- ListBase *nurb,
ListBase *dispbase,
- Mesh **r_final,
const bool for_render,
- const bool force_mesh_conversion)
+ const bool force_mesh_conversion,
+ Mesh **r_final)
{
- VirtualModifierData virtualModifierData;
- ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- ModifierData *pretessellatePoint;
- Curve *cu = ob->data;
- int required_mode = 0, totvert = 0;
+ const Curve *cu = (const Curve *)ob->data;
+
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- Mesh *modified = NULL, *mesh_applied;
- float(*vertCos)[3] = NULL;
- int useCache = !for_render;
- ModifierApplyFlag apply_flag = 0;
+ const bool use_cache = !for_render;
- if (for_render) {
- apply_flag |= MOD_APPLY_RENDER;
- required_mode = eModifierMode_Render;
- }
- else {
- required_mode = eModifierMode_Realtime;
+ ModifierApplyFlag apply_flag = for_render ? MOD_APPLY_RENDER : (ModifierApplyFlag)0;
+ ModifierMode required_mode = for_render ? eModifierMode_Render : eModifierMode_Realtime;
+ if (editmode) {
+ required_mode = (ModifierMode)((int)required_mode | eModifierMode_Editmode);
}
const ModifierEvalContext mectx_deform = {
- depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
+ depsgraph, ob, editmode ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
const ModifierEvalContext mectx_apply = {
- depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
-
- pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
+ depsgraph,
+ ob,
+ use_cache ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
- if (editmode) {
- required_mode |= eModifierMode_Editmode;
- }
+ ModifierData *pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
- if (pretessellatePoint) {
- md = pretessellatePoint->next;
- }
+ VirtualModifierData virtualModifierData;
+ ModifierData *md = pretessellatePoint == nullptr ?
+ BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData) :
+ pretessellatePoint->next;
if (r_final && *r_final) {
- BKE_id_free(NULL, *r_final);
+ BKE_id_free(nullptr, *r_final);
}
+ Mesh *modified = nullptr;
+ float(*vertCos)[3] = nullptr;
for (; md; md = md->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;
}
/* If we need normals, no choice, have to convert to mesh now. */
- bool need_normal = mti->dependsOnNormals != NULL && mti->dependsOnNormals(md);
+ const bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md);
/* XXX 2.8 : now that batch cache is stored inside the ob->data
* we need to create a Mesh for each curve that uses modifiers. */
- if (modified == NULL /* && need_normal */) {
- if (vertCos != NULL) {
+ if (modified == nullptr /* && need_normal */) {
+ if (vertCos != nullptr) {
displist_vert_coords_apply(dispbase, vertCos);
}
if (ELEM(ob->type, OB_CURVE, OB_FONT) && (cu->flag & CU_DEFORM_FILL)) {
- curve_to_filledpoly(cu, nurb, dispbase);
+ curve_to_filledpoly(cu, dispbase);
}
modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase);
@@ -968,6 +930,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (mti->type == eModifierTypeType_OnlyDeform ||
(mti->type == eModifierTypeType_DeformOrConstruct && !modified)) {
if (modified) {
+ int totvert = 0;
if (!vertCos) {
vertCos = BKE_mesh_vert_coords_alloc(modified, &totvert);
}
@@ -977,26 +940,26 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
mti->deformVerts(md, &mectx_deform, modified, vertCos, totvert);
}
else {
+ int totvert = 0;
if (!vertCos) {
vertCos = displist_vert_coords_alloc(dispbase, &totvert);
}
- mti->deformVerts(md, &mectx_deform, NULL, vertCos, totvert);
+ mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert);
}
}
else {
if (!r_final) {
- /* makeDisplistCurveTypes could be used for beveling, where derived mesh
+ /* makeDisplistCurveTypes could be used for beveling, where mesh
* is totally unnecessary, so we could stop modifiers applying
- * when we found constructive modifier but derived mesh is unwanted result
- */
+ * when we found constructive modifier but mesh is unwanted. */
break;
}
if (modified) {
if (vertCos) {
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
- NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
- BKE_id_free(NULL, modified);
+ nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1008,7 +971,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
if (ELEM(ob->type, OB_CURVE, OB_FONT) && (cu->flag & CU_DEFORM_FILL)) {
- curve_to_filledpoly(cu, nurb, dispbase);
+ curve_to_filledpoly(cu, dispbase);
}
modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase);
@@ -1017,19 +980,17 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
/* Vertex coordinates were applied to necessary data, could free it */
MEM_freeN(vertCos);
- vertCos = NULL;
+ vertCos = nullptr;
}
if (need_normal) {
BKE_mesh_ensure_normals(modified);
}
- mesh_applied = mti->modifyMesh(md, &mectx_apply, modified);
+ Mesh *mesh_applied = mti->modifyMesh(md, &mectx_apply, modified);
if (mesh_applied) {
- /* Modifier returned a new derived mesh */
-
- if (modified && modified != mesh_applied) { /* Modifier */
- BKE_id_free(NULL, modified);
+ if (modified && modified != mesh_applied) {
+ BKE_id_free(nullptr, modified);
}
modified = mesh_applied;
}
@@ -1038,8 +999,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
if (modified) {
- Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
- BKE_id_free(NULL, modified);
+ Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
+ nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1050,7 +1012,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
else {
displist_vert_coords_apply(dispbase, vertCos);
MEM_freeN(vertCos);
- vertCos = NULL;
+ vertCos = nullptr;
}
}
@@ -1078,39 +1040,37 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
BKE_mesh_ensure_normals(modified);
/* Special tweaks, needed since neither BKE_mesh_new_nomain_from_template() nor
- * BKE_mesh_new_nomain_from_curve_displist() properly duplicate mat info...
- */
+ * BKE_mesh_new_nomain_from_curve_displist() properly duplicate mat info... */
BLI_strncpy(modified->id.name, cu->id.name, sizeof(modified->id.name));
*((short *)modified->id.name) = ID_ME;
MEM_SAFE_FREE(modified->mat);
/* Set flag which makes it easier to see what's going on in a debugger. */
modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
- modified->mat = MEM_dupallocN(cu->mat);
+ modified->mat = (Material **)MEM_dupallocN(cu->mat);
modified->totcol = cu->totcol;
(*r_final) = modified;
}
else {
- (*r_final) = NULL;
+ (*r_final) = nullptr;
}
}
- else if (modified != NULL) {
+ else if (modified != nullptr) {
/* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */
- BKE_id_free(NULL, modified);
+ BKE_id_free(nullptr, modified);
}
}
static void displist_surf_indices(DispList *dl)
{
- int a, b, p1, p2, p3, p4;
- int *index;
+ int b, p1, p2, p3, p4;
dl->totindex = 0;
- index = dl->index = MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
- "index array nurbs");
+ int *index = dl->index = (int *)MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
+ "index array nurbs");
- for (a = 0; a < dl->parts; a++) {
+ for (int a = 0; a < dl->parts; a++) {
if (BKE_displist_surfindex_get(dl, a, &b, &p1, &p2, &p3, &p4) == 0) {
break;
@@ -1132,28 +1092,25 @@ static void displist_surf_indices(DispList *dl)
}
}
-void BKE_displist_make_surf(Depsgraph *depsgraph,
- Scene *scene,
- Object *ob,
- ListBase *dispbase,
- Mesh **r_final,
- const bool for_render,
- const bool for_orco)
+static void displist_make_surf(Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *ob,
+ ListBase *dispbase,
+ Mesh **r_final,
+ const bool for_render,
+ const bool for_orco)
{
- ListBase nubase = {NULL, NULL};
- Curve *cu = ob->data;
- DispList *dl;
- float *data;
- int len;
- bool force_mesh_conversion = false;
+ ListBase nubase = {nullptr, nullptr};
+ const Curve *cu = (const Curve *)ob->data;
if (!for_render && cu->editnurb) {
- BKE_nurbList_duplicate(&nubase, BKE_curve_editNurbs_get(cu));
+ BKE_nurbList_duplicate(&nubase, BKE_curve_editNurbs_get(const_cast<Curve *>(cu)));
}
else {
BKE_nurbList_duplicate(&nubase, &cu->nurb);
}
+ bool force_mesh_conversion = false;
if (!for_orco) {
force_mesh_conversion = BKE_curve_calc_modifiers_pre(
depsgraph, scene, ob, &nubase, &nubase, for_render);
@@ -1164,34 +1121,23 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
continue;
}
- int resolu = nu->resolu, resolv = nu->resolv;
-
- if (for_render) {
- if (cu->resolu_ren) {
- resolu = cu->resolu_ren;
- }
- if (cu->resolv_ren) {
- resolv = cu->resolv_ren;
- }
- }
+ const int resolu = (for_render && cu->resolu_ren) ? cu->resolu_ren : nu->resolu;
+ const int resolv = (for_render && cu->resolv_ren) ? cu->resolv_ren : nu->resolv;
if (nu->pntsv == 1) {
- len = SEGMENTSU(nu) * resolu;
+ const int len = SEGMENTSU(nu) * resolu;
- dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
dl->nr = len;
dl->col = nu->mat_nr;
dl->charidx = nu->charidx;
+ dl->rt = nu->flag;
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
-
- data = dl->verts;
+ float *data = dl->verts;
if (nu->flagu & CU_NURB_CYCLIC) {
dl->type = DL_POLY;
}
@@ -1199,23 +1145,20 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
dl->type = DL_SEGM;
}
- BKE_nurb_makeCurve(nu, data, NULL, NULL, NULL, resolu, sizeof(float[3]));
+ BKE_nurb_makeCurve(nu, data, nullptr, nullptr, nullptr, resolu, sizeof(float[3]));
}
else {
- len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
+ const int len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
- dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->col = nu->mat_nr;
dl->charidx = nu->charidx;
+ dl->rt = nu->flag;
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
-
- data = dl->verts;
+ float *data = dl->verts;
dl->type = DL_SURF;
dl->parts = (nu->pntsu * resolu); /* in reverse, because makeNurbfaces works that way */
@@ -1237,7 +1180,7 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
if (!for_orco) {
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
curve_calc_modifiers_post(
- depsgraph, scene, ob, &nubase, dispbase, r_final, for_render, force_mesh_conversion);
+ depsgraph, scene, ob, dispbase, for_render, force_mesh_conversion, r_final);
}
BKE_nurbList_free(&nubase);
@@ -1262,7 +1205,7 @@ static void rotateBevelPiece(const Curve *cu,
vec[1] = fp[2];
vec[2] = 0.0;
- if (nbevp == NULL) {
+ if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
copy_qt_qt(quat, bevp->quat);
}
@@ -1280,7 +1223,7 @@ static void rotateBevelPiece(const Curve *cu,
else {
float sina, cosa;
- if (nbevp == NULL) {
+ if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
sina = bevp->sina;
cosa = bevp->cosa;
@@ -1304,12 +1247,13 @@ static void rotateBevelPiece(const Curve *cu,
*r_data = data;
}
-static void fillBevelCap(Nurb *nu, DispList *dlb, float *prev_fp, ListBase *dispbase)
+static void fillBevelCap(const Nurb *nu,
+ const DispList *dlb,
+ const float *prev_fp,
+ ListBase *dispbase)
{
- DispList *dl;
-
- dl = MEM_callocN(sizeof(DispList), "makeDispListbev2");
- dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev2");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
memcpy(dl->verts, prev_fp, sizeof(float[3]) * dlb->nr);
dl->type = DL_POLY;
@@ -1318,18 +1262,15 @@ static void fillBevelCap(Nurb *nu, DispList *dlb, float *prev_fp, ListBase *disp
dl->nr = dlb->nr;
dl->col = nu->mat_nr;
dl->charidx = nu->charidx;
-
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
+ dl->rt = nu->flag;
BLI_addtail(dispbase, dl);
}
static void calc_bevfac_segment_mapping(
- BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
+ const BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
{
- float normlen, normsum = 0.0f;
+ float normsum = 0.0f;
float *seglen = bl->seglen;
int *segbevcount = bl->segbevcount;
int bevcount = 0, nr = bl->nr;
@@ -1338,7 +1279,7 @@ static void calc_bevfac_segment_mapping(
*r_bev = (int)bev_fl;
while (bevcount < nr - 1) {
- normlen = *seglen / spline_length;
+ float normlen = *seglen / spline_length;
if (normsum + normlen > bevfac) {
bev_fl = bevcount + (bevfac - normsum) / normlen * *segbevcount;
*r_bev = (int)bev_fl;
@@ -1353,7 +1294,7 @@ static void calc_bevfac_segment_mapping(
}
static void calc_bevfac_spline_mapping(
- BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
+ const BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
{
const float len_target = bevfac * spline_length;
BevPoint *bevp = bl->bevpoints;
@@ -1375,7 +1316,7 @@ static void calc_bevfac_spline_mapping(
}
static void calc_bevfac_mapping_default(
- BevList *bl, int *r_start, float *r_firstblend, int *r_steps, float *r_lastblend)
+ const BevList *bl, int *r_start, float *r_firstblend, int *r_steps, float *r_lastblend)
{
*r_start = 0;
*r_steps = bl->nr;
@@ -1383,9 +1324,9 @@ static void calc_bevfac_mapping_default(
*r_lastblend = 1.0f;
}
-static void calc_bevfac_mapping(Curve *cu,
- BevList *bl,
- Nurb *nu,
+static void calc_bevfac_mapping(const Curve *cu,
+ const BevList *bl,
+ const Nurb *nu,
int *r_start,
float *r_firstblend,
int *r_steps,
@@ -1463,14 +1404,14 @@ static void calc_bevfac_mapping(Curve *cu,
}
static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
- Scene *scene,
+ const Scene *scene,
Object *ob,
ListBase *dispbase,
const bool for_render,
const bool for_orco,
Mesh **r_final)
{
- Curve *cu = ob->data;
+ const Curve *cu = (const Curve *)ob->data;
/* we do allow duplis... this is only displist on curve level */
if (!ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) {
@@ -1478,259 +1419,246 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
if (ob->type == OB_SURF) {
- BKE_displist_make_surf(depsgraph, scene, ob, dispbase, r_final, for_render, for_orco);
- }
- else if (ELEM(ob->type, OB_CURVE, OB_FONT)) {
- ListBase dlbev;
- ListBase nubase = {NULL, NULL};
- bool force_mesh_conversion = false;
-
- BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
-
- /* We only re-evaluate path if evaluation is not happening for orco.
- * If the calculation happens for orco, we should never free data which
- * was needed before and only not needed for orco calculation.
- */
- if (!for_orco) {
- if (ob->runtime.curve_cache->path) {
- free_path(ob->runtime.curve_cache->path);
- }
- ob->runtime.curve_cache->path = NULL;
- }
+ displist_make_surf(depsgraph, scene, ob, dispbase, r_final, for_render, for_orco);
+ return;
+ }
- if (ob->type == OB_FONT) {
- BKE_vfont_to_curve_nubase(ob, FO_EDIT, &nubase);
- }
- else {
- BKE_nurbList_duplicate(&nubase, BKE_curve_nurbs_get(cu));
- }
+ ListBase nubase = {nullptr, nullptr};
+ bool force_mesh_conversion = false;
+
+ BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
- if (!for_orco) {
- force_mesh_conversion = BKE_curve_calc_modifiers_pre(
- depsgraph, scene, ob, &nubase, &nubase, for_render);
+ /* We only re-evaluate path if evaluation is not happening for orco.
+ * If the calculation happens for orco, we should never free data which
+ * was needed before and only not needed for orco calculation. */
+ if (!for_orco) {
+ if (ob->runtime.curve_cache->anim_path_accum_length) {
+ MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
+ ob->runtime.curve_cache->anim_path_accum_length = nullptr;
+ }
- BKE_curve_bevelList_make(ob, &nubase, for_render);
+ if (ob->type == OB_FONT) {
+ BKE_vfont_to_curve_nubase(ob, FO_EDIT, &nubase);
+ }
+ else {
+ BKE_nurbList_duplicate(&nubase, BKE_curve_nurbs_get(const_cast<Curve *>(cu)));
+ }
- /* If curve has no bevel will return nothing */
- BKE_curve_bevel_make(ob, &dlbev);
+ if (!for_orco) {
+ force_mesh_conversion = BKE_curve_calc_modifiers_pre(
+ depsgraph, scene, ob, &nubase, &nubase, for_render);
+ }
- /* no bevel or extrude, and no width correction? */
- if (!dlbev.first && cu->width == 1.0f) {
- curve_to_displist(cu, &nubase, for_render, dispbase);
- }
- else {
- const float widfac = cu->width - 1.0f;
- BevList *bl = ob->runtime.curve_cache->bev.first;
- Nurb *nu = nubase.first;
+ BKE_curve_bevelList_make(ob, &nubase, for_render);
- for (; bl && nu; bl = bl->next, nu = nu->next) {
- float *data;
+ /* If curve has no bevel will return nothing */
+ ListBase dlbev = BKE_curve_bevel_make(cu);
- if (bl->nr == 0) { /* blank bevel lists can happen */
- continue;
- }
+ /* no bevel or extrude, and no width correction? */
+ if (BLI_listbase_is_empty(&dlbev) && cu->width == 1.0f) {
+ curve_to_displist(cu, &nubase, for_render, dispbase);
+ }
+ else {
+ const float widfac = cu->width - 1.0f;
- /* exception handling; curve without bevel or extrude, with width correction */
- if (BLI_listbase_is_empty(&dlbev)) {
- DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev");
- dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
- BLI_addtail(dispbase, dl);
+ BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first;
+ Nurb *nu = (Nurb *)nubase.first;
+ for (; bl && nu; bl = bl->next, nu = nu->next) {
+ float *data;
- if (bl->poly != -1) {
- dl->type = DL_POLY;
- }
- else {
- dl->type = DL_SEGM;
- dl->flag = (DL_FRONT_CURVE | DL_BACK_CURVE);
- }
+ if (bl->nr == 0) { /* blank bevel lists can happen */
+ continue;
+ }
- dl->parts = 1;
- dl->nr = bl->nr;
- dl->col = nu->mat_nr;
- dl->charidx = nu->charidx;
+ /* exception handling; curve without bevel or extrude, with width correction */
+ if (BLI_listbase_is_empty(&dlbev)) {
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
+ BLI_addtail(dispbase, dl);
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
-
- int a = dl->nr;
- BevPoint *bevp = bl->bevpoints;
- data = dl->verts;
- while (a--) {
- data[0] = bevp->vec[0] + widfac * bevp->sina;
- data[1] = bevp->vec[1] + widfac * bevp->cosa;
- data[2] = bevp->vec[2];
- bevp++;
- data += 3;
- }
+ if (bl->poly != -1) {
+ dl->type = DL_POLY;
}
else {
- ListBase bottom_capbase = {NULL, NULL};
- ListBase top_capbase = {NULL, NULL};
- float bottom_no[3] = {0.0f};
- float top_no[3] = {0.0f};
- float first_blend = 0.0f, last_blend = 0.0f;
- int start, steps = 0;
-
- if (nu->flagu & CU_NURB_CYCLIC) {
- calc_bevfac_mapping_default(bl, &start, &first_blend, &steps, &last_blend);
- }
- else {
- if (fabsf(cu->bevfac2 - cu->bevfac1) < FLT_EPSILON) {
- continue;
- }
-
- calc_bevfac_mapping(cu, bl, nu, &start, &first_blend, &steps, &last_blend);
- }
+ dl->type = DL_SEGM;
+ dl->flag = (DL_FRONT_CURVE | DL_BACK_CURVE);
+ }
- LISTBASE_FOREACH (DispList *, dlb, &dlbev) {
- /* for each part of the bevel use a separate displblock */
- DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev1");
- dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
- BLI_addtail(dispbase, dl);
+ dl->parts = 1;
+ dl->nr = bl->nr;
+ dl->col = nu->mat_nr;
+ dl->charidx = nu->charidx;
+ dl->rt = nu->flag;
- dl->type = DL_SURF;
+ int a = dl->nr;
+ BevPoint *bevp = bl->bevpoints;
+ data = dl->verts;
+ while (a--) {
+ data[0] = bevp->vec[0] + widfac * bevp->sina;
+ data[1] = bevp->vec[1] + widfac * bevp->cosa;
+ data[2] = bevp->vec[2];
+ bevp++;
+ data += 3;
+ }
+ }
+ else {
+ ListBase bottom_capbase = {nullptr, nullptr};
+ ListBase top_capbase = {nullptr, nullptr};
+ float bottom_no[3] = {0.0f};
+ float top_no[3] = {0.0f};
+ float first_blend = 0.0f, last_blend = 0.0f;
+ int start, steps = 0;
+
+ if (nu->flagu & CU_NURB_CYCLIC) {
+ calc_bevfac_mapping_default(bl, &start, &first_blend, &steps, &last_blend);
+ }
+ else {
+ if (fabsf(cu->bevfac2 - cu->bevfac1) < FLT_EPSILON) {
+ continue;
+ }
- dl->flag = dlb->flag & (DL_FRONT_CURVE | DL_BACK_CURVE);
- if (dlb->type == DL_POLY) {
- dl->flag |= DL_CYCL_U;
- }
- if ((bl->poly >= 0) && (steps > 2)) {
- dl->flag |= DL_CYCL_V;
- }
+ calc_bevfac_mapping(cu, bl, nu, &start, &first_blend, &steps, &last_blend);
+ }
- dl->parts = steps;
- dl->nr = dlb->nr;
- dl->col = nu->mat_nr;
- dl->charidx = nu->charidx;
+ LISTBASE_FOREACH (DispList *, dlb, &dlbev) {
+ /* for each part of the bevel use a separate displblock */
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1");
+ dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
+ BLI_addtail(dispbase, dl);
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
+ dl->type = DL_SURF;
- dl->bevel_split = BLI_BITMAP_NEW(steps, "bevel_split");
+ dl->flag = dlb->flag & (DL_FRONT_CURVE | DL_BACK_CURVE);
+ if (dlb->type == DL_POLY) {
+ dl->flag |= DL_CYCL_U;
+ }
+ if ((bl->poly >= 0) && (steps > 2)) {
+ dl->flag |= DL_CYCL_V;
+ }
- /* for each point of poly make a bevel piece */
- BevPoint *bevp_first = bl->bevpoints;
- BevPoint *bevp_last = &bl->bevpoints[bl->nr - 1];
- BevPoint *bevp = &bl->bevpoints[start];
- for (int i = start, a = 0; a < steps; i++, bevp++, a++) {
- float radius_factor = 1.0;
- float *cur_data = data;
+ dl->parts = steps;
+ dl->nr = dlb->nr;
+ dl->col = nu->mat_nr;
+ dl->charidx = nu->charidx;
+ dl->rt = nu->flag;
+
+ /* for each point of poly make a bevel piece */
+ BevPoint *bevp_first = bl->bevpoints;
+ BevPoint *bevp_last = &bl->bevpoints[bl->nr - 1];
+ BevPoint *bevp = &bl->bevpoints[start];
+ for (int i = start, a = 0; a < steps; i++, bevp++, a++) {
+ float radius_factor = 1.0;
+ float *cur_data = data;
+
+ if (cu->taperobj == nullptr) {
+ radius_factor = bevp->radius;
+ }
+ else {
+ float taper_factor;
+ if (cu->flag & CU_MAP_TAPER) {
+ float len = (steps - 3) + first_blend + last_blend;
- if (cu->taperobj == NULL) {
- radius_factor = bevp->radius;
- }
- else {
- float taper_factor;
- if (cu->flag & CU_MAP_TAPER) {
- float len = (steps - 3) + first_blend + last_blend;
-
- if (a == 0) {
- taper_factor = 0.0f;
- }
- else if (a == steps - 1) {
- taper_factor = 1.0f;
- }
- else {
- taper_factor = ((float)a - (1.0f - first_blend)) / len;
- }
+ if (a == 0) {
+ taper_factor = 0.0f;
+ }
+ else if (a == steps - 1) {
+ taper_factor = 1.0f;
}
else {
- float len = bl->nr - 1;
- taper_factor = (float)i / len;
-
- if (a == 0) {
- taper_factor += (1.0f - first_blend) / len;
- }
- else if (a == steps - 1) {
- taper_factor -= (1.0f - last_blend) / len;
- }
+ taper_factor = ((float)a - (1.0f - first_blend)) / len;
}
+ }
+ else {
+ float len = bl->nr - 1;
+ taper_factor = (float)i / len;
- radius_factor = displist_calc_taper(depsgraph, scene, cu->taperobj, taper_factor);
-
- if (cu->taper_radius_mode == CU_TAPER_RADIUS_MULTIPLY) {
- radius_factor *= bevp->radius;
+ if (a == 0) {
+ taper_factor += (1.0f - first_blend) / len;
}
- else if (cu->taper_radius_mode == CU_TAPER_RADIUS_ADD) {
- radius_factor += bevp->radius;
+ else if (a == steps - 1) {
+ taper_factor -= (1.0f - last_blend) / len;
}
}
- if (bevp->split_tag) {
- BLI_BITMAP_ENABLE(dl->bevel_split, a);
- }
+ radius_factor = displist_calc_taper(depsgraph, scene, cu->taperobj, taper_factor);
- /* rotate bevel piece and write in data */
- if ((a == 0) && (bevp != bevp_last)) {
- rotateBevelPiece(
- cu, bevp, bevp + 1, dlb, 1.0f - first_blend, widfac, radius_factor, &data);
+ if (cu->taper_radius_mode == CU_TAPER_RADIUS_MULTIPLY) {
+ radius_factor *= bevp->radius;
}
- else if ((a == steps - 1) && (bevp != bevp_first)) {
- rotateBevelPiece(
- cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data);
- }
- else {
- rotateBevelPiece(cu, bevp, NULL, dlb, 0.0f, widfac, radius_factor, &data);
+ else if (cu->taper_radius_mode == CU_TAPER_RADIUS_ADD) {
+ radius_factor += bevp->radius;
}
+ }
- if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) {
- if (a == 1) {
- fillBevelCap(nu, dlb, cur_data - 3 * dlb->nr, &bottom_capbase);
- copy_v3_v3(bottom_no, bevp->dir);
- }
- if (a == steps - 1) {
- fillBevelCap(nu, dlb, cur_data, &top_capbase);
- negate_v3_v3(top_no, bevp->dir);
- }
- }
+ /* rotate bevel piece and write in data */
+ if ((a == 0) && (bevp != bevp_last)) {
+ rotateBevelPiece(
+ cu, bevp, bevp + 1, dlb, 1.0f - first_blend, widfac, radius_factor, &data);
+ }
+ else if ((a == steps - 1) && (bevp != bevp_first)) {
+ rotateBevelPiece(
+ cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data);
+ }
+ else {
+ rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data);
}
- /* gl array drawing: using indices */
- displist_surf_indices(dl);
+ if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) {
+ if (a == 1) {
+ fillBevelCap(nu, dlb, cur_data - 3 * dlb->nr, &bottom_capbase);
+ copy_v3_v3(bottom_no, bevp->dir);
+ }
+ if (a == steps - 1) {
+ fillBevelCap(nu, dlb, cur_data, &top_capbase);
+ negate_v3_v3(top_no, bevp->dir);
+ }
+ }
}
- if (bottom_capbase.first) {
- BKE_displist_fill(&bottom_capbase, dispbase, bottom_no, false);
- BKE_displist_fill(&top_capbase, dispbase, top_no, false);
- BKE_displist_free(&bottom_capbase);
- BKE_displist_free(&top_capbase);
- }
+ /* gl array drawing: using indices */
+ displist_surf_indices(dl);
}
- }
- BKE_displist_free(&dlbev);
- }
- if (!(cu->flag & CU_DEFORM_FILL)) {
- curve_to_filledpoly(cu, &nubase, dispbase);
+ if (bottom_capbase.first) {
+ BKE_displist_fill(&bottom_capbase, dispbase, bottom_no, false);
+ BKE_displist_fill(&top_capbase, dispbase, top_no, false);
+ BKE_displist_free(&bottom_capbase);
+ BKE_displist_free(&top_capbase);
+ }
+ }
}
+ BKE_displist_free(&dlbev);
+ }
- if (!for_orco) {
- if ((cu->flag & CU_PATH) ||
- DEG_get_eval_flags_for_id(depsgraph, &ob->id) & DAG_EVAL_NEED_CURVE_PATH) {
- calc_curvepath(ob, &nubase);
- }
+ if (!(cu->flag & CU_DEFORM_FILL)) {
+ curve_to_filledpoly(cu, dispbase);
+ }
- BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
- curve_calc_modifiers_post(
- depsgraph, scene, ob, &nubase, dispbase, r_final, for_render, force_mesh_conversion);
+ if (!for_orco) {
+ if ((cu->flag & CU_PATH) ||
+ DEG_get_eval_flags_for_id(depsgraph, &ob->id) & DAG_EVAL_NEED_CURVE_PATH) {
+ BKE_anim_path_calc_data(ob);
}
- if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) {
- curve_to_filledpoly(cu, &nubase, dispbase);
- }
+ BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
+ curve_calc_modifiers_post(
+ depsgraph, scene, ob, dispbase, for_render, force_mesh_conversion, r_final);
+ }
- BKE_nurbList_free(&nubase);
+ if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) {
+ curve_to_filledpoly(cu, dispbase);
}
+
+ BKE_nurbList_free(&nubase);
}
-void BKE_displist_make_curveTypes(
- Depsgraph *depsgraph, Scene *scene, Object *ob, const bool for_render, const bool for_orco)
+void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *ob,
+ const bool for_render,
+ const bool for_orco)
{
- ListBase *dispbase;
-
/* The same check for duplis as in do_makeDispListCurveTypes.
* Happens when curve used for constraint/bevel was converted to mesh.
* check there is still needed for render displist and orco displists. */
@@ -1741,15 +1669,16 @@ void BKE_displist_make_curveTypes(
BKE_object_free_derived_caches(ob);
if (!ob->runtime.curve_cache) {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types");
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for curve types");
}
- dispbase = &(ob->runtime.curve_cache->disp);
+ ListBase *dispbase = &(ob->runtime.curve_cache->disp);
- Mesh *mesh_eval = NULL;
+ Mesh *mesh_eval = nullptr;
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval);
- if (mesh_eval != NULL) {
+ if (mesh_eval != nullptr) {
BKE_object_eval_assign_data(ob, &mesh_eval->id, true);
}
@@ -1757,32 +1686,32 @@ void BKE_displist_make_curveTypes(
}
void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph,
- Scene *scene,
+ const Scene *scene,
Object *ob,
ListBase *dispbase,
- Mesh **r_final,
- const bool for_orco)
+ const bool for_orco,
+ Mesh **r_final)
{
- if (ob->runtime.curve_cache == NULL) {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+ if (ob->runtime.curve_cache == nullptr) {
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for Curve");
}
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final);
}
-void BKE_displist_minmax(ListBase *dispbase, float min[3], float max[3])
+void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
{
- const float *vert;
- int a, tot = 0;
- int doit = 0;
+ bool doit = false;
- LISTBASE_FOREACH (DispList *, dl, dispbase) {
- tot = (dl->type == DL_INDEX3) ? dl->nr : dl->nr * dl->parts;
- vert = dl->verts;
- for (a = 0; a < tot; a++, vert += 3) {
- minmax_v3v3_v3(min, max, vert);
+ LISTBASE_FOREACH (const DispList *, dl, dispbase) {
+ const int tot = (dl->type == DL_INDEX3) ? dl->nr : dl->nr * dl->parts;
+ for (const int i : IndexRange(tot)) {
+ minmax_v3v3_v3(min, max, &dl->verts[i]);
+ }
+ if (tot != 0) {
+ doit = true;
}
- doit |= (tot != 0);
}
if (!doit) {
@@ -1797,12 +1726,11 @@ static void boundbox_displist_object(Object *ob)
{
if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
/* Curve's BB is already calculated as a part of modifier stack,
- * here we only calculate object BB based on final display list.
- */
+ * here we only calculate object BB based on final display list. */
/* object's BB is calculated from final displist */
- if (ob->runtime.bb == NULL) {
- ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox");
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox");
}
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index 4a25b0e9d98..42af3a391ed 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -1702,7 +1702,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface
}
for (int i = 0; i < totloop; i++) {
- rgba_uchar_to_float(pPoint[mloop[i].v].color, (const unsigned char *)&col[mloop[i].v].r);
+ rgba_uchar_to_float(pPoint[mloop[i].v].color, (const unsigned char *)&col[i].r);
}
}
else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c
index 0fa3bb29ccd..472de1f3c77 100644
--- a/source/blender/blenkernel/intern/editmesh.c
+++ b/source/blender/blenkernel/intern/editmesh.c
@@ -127,9 +127,10 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em)
}
em->looptris = looptris;
+ em->tottri = looptris_tot;
/* after allocating the em->looptris, we're ready to tessellate */
- BM_mesh_calc_tessellation(em->bm, em->looptris, &em->tottri);
+ BM_mesh_calc_tessellation(em->bm, em->looptris);
}
void BKE_editmesh_looptri_calc(BMEditMesh *em)
@@ -148,6 +149,14 @@ void BKE_editmesh_looptri_calc(BMEditMesh *em)
#endif
}
+void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo)
+{
+ BLI_assert(em->tottri == poly_to_tri_count(em->bm->totface, em->bm->totloop));
+ BLI_assert(em->looptris != NULL);
+
+ BM_mesh_calc_tessellation_with_partial(em->bm, em->looptris, bmpinfo);
+}
+
void BKE_editmesh_free_derivedmesh(BMEditMesh *em)
{
if (em->mesh_eval_cage) {
diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c
index d849f4ab37d..088a2087a96 100644
--- a/source/blender/blenkernel/intern/editmesh_tangent.c
+++ b/source/blender/blenkernel/intern/editmesh_tangent.c
@@ -362,7 +362,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
/* Calculation */
if (em->tottri != 0) {
TaskPool *task_pool;
- task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
tangent_mask_curr = 0;
/* Calculate tangent layers */
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 4104b6080c5..e39749225ea 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -161,13 +161,13 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef
if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVE) {
Curve *cu = eff->ob->data;
if (cu->flag & CU_PATH) {
- if (eff->ob->runtime.curve_cache == NULL || eff->ob->runtime.curve_cache->path == NULL ||
- eff->ob->runtime.curve_cache->path->data == NULL) {
+ if (eff->ob->runtime.curve_cache == NULL ||
+ eff->ob->runtime.curve_cache->anim_path_accum_length == NULL) {
BKE_displist_make_curveTypes(depsgraph, eff->scene, eff->ob, false, false);
}
- if (eff->ob->runtime.curve_cache->path && eff->ob->runtime.curve_cache->path->data) {
- where_on_path(
+ if (eff->ob->runtime.curve_cache->anim_path_accum_length) {
+ BKE_where_on_path(
eff->ob, 0.0, eff->guide_loc, eff->guide_dir, NULL, &eff->guide_radius, NULL);
mul_m4_v3(eff->ob->obmat, eff->guide_loc);
mul_mat3_m4_v3(eff->ob->obmat, eff->guide_dir);
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 8e1fa9732ea..68ed3c239ef 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -35,7 +35,9 @@
#include "BLI_blenlib.h"
#include "BLI_easing.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
+#include "BLI_sort_utils.h"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -179,8 +181,10 @@ void BKE_fcurves_copy(ListBase *dst, ListBase *src)
}
}
-/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
- * `IDTypeInfo` structure). */
+/**
+ * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
+ * `IDTypeInfo` structure).
+ */
void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data)
{
ChannelDriver *driver = fcu->driver;
@@ -290,6 +294,12 @@ FCurve *BKE_fcurve_find(ListBase *list, const char rna_path[], const int array_i
return NULL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name FCurve Iteration
+ * \{ */
+
/* Quick way to loop over all fcurves of a given 'path'. */
FCurve *BKE_fcurve_iter_step(FCurve *fcu_iter, const char rna_path[])
{
@@ -829,6 +839,56 @@ bool BKE_fcurve_calc_range(
return foundvert;
}
+/**
+ * Return an array of keyed frames, rounded to `interval`.
+ *
+ * \param interval: Set to 1.0 to round to whole keyframes, 0.5 for in-between key-frames, etc.
+ *
+ * \note An interval of zero could be supported (this implies no rounding at all),
+ * however this risks very small differences in float values being treated as separate keyframes.
+ */
+float *BKE_fcurves_calc_keyed_frames_ex(FCurve **fcurve_array,
+ int fcurve_array_len,
+ const float interval,
+ int *r_frames_len)
+{
+ /* Use `1e-3f` as the smallest possible value since these are converted to integers
+ * and we can be sure `MAXFRAME / 1e-3f < INT_MAX` as it's around half the size. */
+ const double interval_db = max_ff(interval, 1e-3f);
+ GSet *frames_unique = BLI_gset_int_new(__func__);
+ for (int fcurve_index = 0; fcurve_index < fcurve_array_len; fcurve_index++) {
+ const FCurve *fcu = fcurve_array[fcurve_index];
+ for (int i = 0; i < fcu->totvert; i++) {
+ const BezTriple *bezt = &fcu->bezt[i];
+ const double value = round((double)bezt->vec[1][0] / interval_db);
+ BLI_assert(value > INT_MIN && value < INT_MAX);
+ BLI_gset_add(frames_unique, POINTER_FROM_INT((int)value));
+ }
+ }
+
+ const size_t frames_len = BLI_gset_len(frames_unique);
+ float *frames = MEM_mallocN(sizeof(*frames) * frames_len, __func__);
+
+ GSetIterator gs_iter;
+ int i = 0;
+ GSET_ITER_INDEX (gs_iter, frames_unique, i) {
+ const int value = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
+ frames[i] = (double)value * interval_db;
+ }
+ BLI_gset_free(frames_unique, NULL);
+
+ qsort(frames, frames_len, sizeof(*frames), BLI_sortutil_cmp_float);
+ *r_frames_len = frames_len;
+ return frames;
+}
+
+float *BKE_fcurves_calc_keyed_frames(FCurve **fcurve_array,
+ int fcurve_array_len,
+ int *r_frames_len)
+{
+ return BKE_fcurves_calc_keyed_frames_ex(fcurve_array, fcurve_array_len, 1.0f, r_frames_len);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1450,7 +1510,8 @@ bool test_time_fcurve(FCurve *fcu)
/** \name F-Curve Calculations
* \{ */
-/* The length of each handle is not allowed to be more
+/**
+ * The length of each handle is not allowed to be more
* than the horizontal distance between (v1-v4).
* This is to prevent curve loops.
*
@@ -1497,9 +1558,12 @@ void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], con
}
}
-/** Find roots of cubic equation (c0 x³ + c1 x² + c2 x + c3)
+/**
+ * Find roots of cubic equation (c0 x^3 + c1 x^2 + c2 x + c3)
* \return number of roots in `o`.
- * NOTE: it is up to the caller to allocate enough memory for `o`. */
+ *
+ * \note it is up to the caller to allocate enough memory for `o`.
+ */
static int solve_cubic(double c0, double c1, double c2, double c3, float *o)
{
double a, b, c, p, q, d, t, phi;
diff --git a/source/blender/blenkernel/intern/fcurve_cache.c b/source/blender/blenkernel/intern/fcurve_cache.c
new file mode 100644
index 00000000000..8142b871edd
--- /dev/null
+++ b/source/blender/blenkernel/intern/fcurve_cache.c
@@ -0,0 +1,189 @@
+/*
+ * 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
+ *
+ * Cache F-Curve look-ups.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+
+#include "BKE_fcurve.h"
+
+/* -------------------------------------------------------------------- */
+/** \name F-Curve Path Cache
+ *
+ * Cache for finding curves by RNA path & array index.
+ * \{ */
+
+struct FCurvePathCache_Span {
+ /** Index in the #FCurvePathCache.fcurve_array indicating the start of the span. */
+ uint index;
+ /** Number of items in the span in #FCurvePathCache.fcurve_array that share an RNA path. */
+ uint len;
+};
+
+struct FCurvePathCache {
+ /** All curves sorted by (#FCurve.rna_path, #FCurve.array_index) */
+ FCurve **fcurve_array;
+ uint fcurve_array_len;
+ /** Storage for values of `span_from_rna_path`. */
+ struct FCurvePathCache_Span *span_table;
+ /** Map `FCurve.rna_path` to elements in #FCurvePathCache.span_table */
+ GHash *span_from_rna_path;
+};
+
+/**
+ * #qsort callback for an #FCurve array.
+ */
+static int fcurve_cmp_for_cache(const void *fcu_a_p, const void *fcu_b_p)
+{
+ const FCurve *fcu_a = *((const FCurve **)fcu_a_p);
+ const FCurve *fcu_b = *((const FCurve **)fcu_b_p);
+ const int cmp = strcmp(fcu_a->rna_path, fcu_b->rna_path);
+ if (cmp != 0) {
+ return cmp;
+ }
+ if (fcu_a->array_index < fcu_b->array_index) {
+ return -1;
+ }
+ if (fcu_a->array_index > fcu_b->array_index) {
+ return 1;
+ }
+ return 0;
+}
+
+struct FCurvePathCache *BKE_fcurve_pathcache_create(ListBase *list)
+{
+ const uint fcurve_array_len = BLI_listbase_count(list);
+ FCurve **fcurve_array = MEM_mallocN(sizeof(*fcurve_array) * fcurve_array_len, __func__);
+ uint i;
+ LISTBASE_FOREACH_INDEX (FCurve *, fcu, list, i) {
+ fcurve_array[i] = fcu;
+ }
+ qsort(fcurve_array, fcurve_array_len, sizeof(FCurve *), fcurve_cmp_for_cache);
+
+ /* Allow for the case no F-curves share an RNA-path, otherwise this is over-allocated.
+ * Although in practice it's likely to only be 3-4x as large as is needed
+ * (with transform channels for e.g.). */
+ struct FCurvePathCache_Span *span_table = MEM_mallocN(sizeof(*span_table) * fcurve_array_len,
+ __func__);
+
+ /* May over reserve, harmless. */
+ GHash *span_from_rna_path = BLI_ghash_str_new_ex(__func__, fcurve_array_len);
+ uint span_index = 0;
+ i = 0;
+ while (i < fcurve_array_len) {
+ uint i_end;
+ for (i_end = i + 1; i_end < fcurve_array_len; i_end++) {
+ /* As the indices are sorted, we know a decrease means a new RNA path is found. */
+ if (fcurve_array[i]->array_index > fcurve_array[i_end]->array_index) {
+ BLI_assert(!STREQ(fcurve_array[i]->rna_path, fcurve_array[i_end]->rna_path));
+ break;
+ }
+ if (!STREQ(fcurve_array[i]->rna_path, fcurve_array[i_end]->rna_path)) {
+ break;
+ }
+ }
+
+ struct FCurvePathCache_Span *span = &span_table[span_index++];
+ span->index = i;
+ span->len = i_end - i;
+ BLI_ghash_insert(span_from_rna_path, fcurve_array[i]->rna_path, span);
+ i = i_end;
+ }
+
+ struct FCurvePathCache *fcache = MEM_callocN(sizeof(struct FCurvePathCache), __func__);
+ fcache->fcurve_array = fcurve_array;
+ fcache->fcurve_array_len = fcurve_array_len;
+ fcache->span_table = span_table;
+ fcache->span_from_rna_path = span_from_rna_path;
+
+ return fcache;
+}
+
+void BKE_fcurve_pathcache_destroy(struct FCurvePathCache *fcache)
+{
+ MEM_freeN(fcache->fcurve_array);
+ MEM_freeN(fcache->span_table);
+ BLI_ghash_free(fcache->span_from_rna_path, NULL, NULL);
+ MEM_freeN(fcache);
+}
+
+FCurve *BKE_fcurve_pathcache_find(struct FCurvePathCache *fcache,
+ const char *rna_path,
+ const int array_index)
+{
+ const struct FCurvePathCache_Span *span = BLI_ghash_lookup(fcache->span_from_rna_path, rna_path);
+ if (span == NULL) {
+ return NULL;
+ }
+
+ FCurve **fcurve = fcache->fcurve_array + span->index;
+ const uint len = span->len;
+ for (int i = 0; i < len; i++) {
+ if (fcurve[i]->array_index == array_index) {
+ return fcurve[i];
+ }
+ /* As these are sorted, early exit. */
+ if (fcurve[i]->array_index > array_index) {
+ break;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Fill in an array of F-Curve, leave NULL when not found.
+ *
+ * \return The number of F-Curves found.
+ */
+int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache,
+ const char *rna_path,
+ FCurve **fcurve_result,
+ int fcurve_result_len)
+{
+ memset(fcurve_result, 0x0, sizeof(*fcurve_result) * fcurve_result_len);
+
+ const struct FCurvePathCache_Span *span = BLI_ghash_lookup(fcache->span_from_rna_path, rna_path);
+ if (span == NULL) {
+ return 0;
+ }
+
+ int found = 0;
+ FCurve **fcurve = fcache->fcurve_array + span->index;
+ const uint len = span->len;
+ for (int i = 0; i < len; i++) {
+ /* As these are sorted, early exit. */
+ if ((uint)fcurve[i]->array_index > (uint)fcurve_result_len) {
+ break;
+ }
+ fcurve_result[fcurve[i]->array_index] = fcurve[i];
+ found += 1;
+ }
+ return found;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 851d8aae378..493a267c2f0 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -623,7 +623,8 @@ static void clamp_bounds_in_domain(FluidDomainSettings *fds,
static bool is_static_object(Object *ob)
{
/* Check if the object has modifiers that might make the object "dynamic". */
- ModifierData *md = ob->modifiers.first;
+ VirtualModifierData virtualModifierData;
+ ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
for (; md; md = md->next) {
if (ELEM(md->type,
eModifierType_Cloth,
@@ -631,7 +632,8 @@ static bool is_static_object(Object *ob)
eModifierType_Explode,
eModifierType_Ocean,
eModifierType_ShapeKey,
- eModifierType_Softbody)) {
+ eModifierType_Softbody,
+ eModifierType_Nodes)) {
return false;
}
}
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index febc9e24c9f..92cc3c763b6 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -447,7 +447,6 @@ static void build_underline(Curve *cu,
nu2->resolu = cu->resolu;
nu2->bezt = NULL;
nu2->knotsu = nu2->knotsv = NULL;
- nu2->flag = CU_2D;
nu2->charidx = charidx + 1000;
if (mat_nr > 0) {
nu2->mat_nr = mat_nr - 1;
@@ -1276,7 +1275,7 @@ static bool vfont_to_curve(Object *ob,
if (cu->textoncurve && cu->textoncurve->type == OB_CURVE) {
BLI_assert(cu->textoncurve->runtime.curve_cache != NULL);
if (cu->textoncurve->runtime.curve_cache != NULL &&
- cu->textoncurve->runtime.curve_cache->path != NULL) {
+ cu->textoncurve->runtime.curve_cache->anim_path_accum_length != NULL) {
float distfac, imat[4][4], imat3[3][3], cmat[3][3];
float minx, maxx;
float timeofs, sizefac;
@@ -1310,7 +1309,8 @@ static bool vfont_to_curve(Object *ob,
/* length correction */
const float chartrans_size_x = maxx - minx;
if (chartrans_size_x != 0.0f) {
- const float totdist = cu->textoncurve->runtime.curve_cache->path->totdist;
+ const CurveCache *cc = cu->textoncurve->runtime.curve_cache;
+ const float totdist = BKE_anim_path_get_length(cc);
distfac = (sizefac * totdist) / chartrans_size_x;
distfac = (distfac > 1.0f) ? (1.0f / distfac) : 1.0f;
}
@@ -1364,8 +1364,8 @@ static bool vfont_to_curve(Object *ob,
/* calc the right loc AND the right rot separately */
/* vec, tvec need 4 items */
- where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
- where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
+ BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
+ BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
mul_v3_fl(vec, sizefac);
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
new file mode 100644
index 00000000000..de8dc355557
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -0,0 +1,1199 @@
+/*
+ * 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 "DNA_ID_enums.h"
+#include "DNA_curve_types.h"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+#include "BKE_curve.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
+#include "BKE_spline.hh"
+
+#include "attribute_access_intern.hh"
+
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_GSpan;
+using blender::fn::GVArrayPtr;
+using blender::fn::GVMutableArray_For_GMutableSpan;
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component Implementation
+ * \{ */
+
+CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
+{
+}
+
+CurveComponent::~CurveComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *CurveComponent::copy() const
+{
+ CurveComponent *new_component = new CurveComponent();
+ if (curve_ != nullptr) {
+ new_component->curve_ = new CurveEval(*curve_);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void CurveComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (curve_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ delete curve_;
+ }
+ if (curve_for_render_ != nullptr) {
+ BKE_id_free(nullptr, curve_for_render_);
+ curve_for_render_ = nullptr;
+ }
+
+ curve_ = nullptr;
+ }
+}
+
+bool CurveComponent::has_curve() const
+{
+ return curve_ != nullptr;
+}
+
+/* Clear the component and replace it with the new curve. */
+void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ curve_ = curve;
+ ownership_ = ownership;
+}
+
+CurveEval *CurveComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ CurveEval *curve = curve_;
+ curve_ = nullptr;
+ return curve;
+}
+
+const CurveEval *CurveComponent::get_for_read() const
+{
+ return curve_;
+}
+
+CurveEval *CurveComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ curve_ = new CurveEval(*curve_);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return curve_;
+}
+
+bool CurveComponent::is_empty() const
+{
+ return curve_ == nullptr;
+}
+
+bool CurveComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void CurveComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ curve_ = new CurveEval(*curve_);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
+/**
+ * Create empty curve data used for rendering the spline's wire edges.
+ * \note See comment on #curve_for_render_ for further explanation.
+ */
+const Curve *CurveComponent::get_curve_for_render() const
+{
+ if (curve_ == nullptr) {
+ return nullptr;
+ }
+ if (curve_for_render_ != nullptr) {
+ return curve_for_render_;
+ }
+ std::lock_guard lock{curve_for_render_mutex_};
+ if (curve_for_render_ != nullptr) {
+ return curve_for_render_;
+ }
+
+ curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU, nullptr);
+ curve_for_render_->curve_eval = curve_;
+
+ return curve_for_render_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Access Helper Functions
+ * \{ */
+
+int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ if (curve_ == nullptr) {
+ return 0;
+ }
+ if (domain == ATTR_DOMAIN_POINT) {
+ int total = 0;
+ for (const SplinePtr &spline : curve_->splines()) {
+ total += spline->size();
+ }
+ return total;
+ }
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return curve_->splines().size();
+ }
+ return 0;
+}
+
+namespace blender::bke {
+
+namespace {
+struct PointIndices {
+ int spline_index;
+ int point_index;
+};
+} // namespace
+static PointIndices lookup_point_indices(Span<int> offsets, const int index)
+{
+ const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) -
+ offsets.begin() - 1;
+ const int index_in_spline = index - offsets[spline_index];
+ return {spline_index, index_in_spline};
+}
+
+/**
+ * Mix together all of a spline's control point values.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<typename T>
+static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
+ const VArray<T> &old_values,
+ MutableSpan<T> r_values)
+{
+ const int splines_len = curve.splines().size();
+ Array<int> offsets = curve.control_point_offsets();
+ BLI_assert(r_values.size() == splines_len);
+ attribute_math::DefaultMixer<T> mixer(r_values);
+
+ for (const int i_spline : IndexRange(splines_len)) {
+ const int spline_offset = offsets[i_spline];
+ const int spline_point_len = offsets[i_spline + 1] - spline_offset;
+ for (const int i_point : IndexRange(spline_point_len)) {
+ const T value = old_values[spline_offset + i_point];
+ mixer.mix_in(i_spline, value);
+ }
+ }
+
+ mixer.finalize();
+}
+
+static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray)
+{
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ Array<T> values(curve.splines().size());
+ adapt_curve_domain_point_to_spline_impl<T>(curve, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
+ }
+ });
+ return new_varray;
+}
+
+/**
+ * A virtual array implementation for the conversion of spline attributes to control point
+ * attributes. The goal is to avoid copying the spline value for every one of its control points
+ * unless it is necessary (in that case the materialize functions will be called).
+ */
+template<typename T> class VArray_For_SplineToPoint final : public VArray<T> {
+ /* Store existing data materialized if it was not already a span. This is expected
+ * to be worth it because a single spline's value will likely be accessed many times. */
+ VArray_Span<T> original_data_;
+ Array<int> offsets_;
+
+ public:
+ VArray_For_SplineToPoint(const VArray<T> &original_varray, Array<int> offsets)
+ : VArray<T>(offsets.last()), original_data_(original_varray), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return original_data_[indices.spline_index];
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ const int total_size = offsets_.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : original_data_.index_range()) {
+ const int offset = offsets_[spline_index];
+ const int next_offset = offsets_[spline_index + 1];
+ r_span.slice(offset, next_offset - offset).fill(original_data_[spline_index]);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int dst_index : mask) {
+ while (offsets_[spline_index] < dst_index) {
+ spline_index++;
+ }
+ r_span[dst_index] = original_data_[spline_index];
+ }
+ }
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ T *dst = r_span.data();
+ const int total_size = offsets_.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : original_data_.index_range()) {
+ const int offset = offsets_[spline_index];
+ const int next_offset = offsets_[spline_index + 1];
+ uninitialized_fill_n(dst + offset, next_offset - offset, original_data_[spline_index]);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int dst_index : mask) {
+ while (offsets_[spline_index] < dst_index) {
+ spline_index++;
+ }
+ new (dst + dst_index) T(original_data_[spline_index]);
+ }
+ }
+ }
+};
+
+static GVArrayPtr adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArrayPtr varray)
+{
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+
+ Array<int> offsets = curve.control_point_offsets();
+ new_varray = std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplineToPoint<T>>>(
+ offsets.last(), *varray->typed<T>(), std::move(offsets));
+ });
+ return new_varray;
+}
+
+} // namespace blender::bke
+
+GVArrayPtr CurveComponent::attribute_try_adapt_domain(GVArrayPtr varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
+{
+ if (!varray) {
+ return {};
+ }
+ if (varray->size() == 0) {
+ return {};
+ }
+ if (from_domain == to_domain) {
+ return varray;
+ }
+
+ if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) {
+ return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray));
+ }
+ if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) {
+ return blender::bke::adapt_curve_domain_spline_to_point(*curve_, std::move(varray));
+ }
+
+ return {};
+}
+
+static CurveEval *get_curve_from_component_for_write(GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
+ CurveComponent &curve_component = static_cast<CurveComponent &>(component);
+ return curve_component.get_for_write();
+}
+
+static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return curve_component.get_for_read();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Builtin Spline Attributes
+ *
+ * Attributes with a value for every spline, stored contiguously or in every spline separately.
+ * \{ */
+
+namespace blender::bke {
+
+class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
+ using AsReadAttribute = GVArrayPtr (*)(const CurveEval &data);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(CurveEval &data);
+ const AsReadAttribute as_read_attribute_;
+ const AsWriteAttribute as_write_attribute_;
+
+ public:
+ BuiltinSplineAttributeProvider(std::string attribute_name,
+ const CustomDataType attribute_type,
+ const WritableEnum writable,
+ const AsReadAttribute as_read_attribute,
+ const AsWriteAttribute as_write_attribute)
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ ATTR_DOMAIN_CURVE,
+ attribute_type,
+ BuiltinAttributeProvider::NonCreatable,
+ writable,
+ BuiltinAttributeProvider::NonDeletable),
+ as_read_attribute_(as_read_attribute),
+ as_write_attribute_(as_write_attribute)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+ return as_read_attribute_(*curve);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ {
+ if (writable_ != Writable) {
+ return {};
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+ return as_write_attribute_(*curve);
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
+ }
+};
+
+static int get_spline_resolution(const SplinePtr &spline)
+{
+ if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) {
+ return bezier_spline->resolution();
+ }
+ if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) {
+ return nurb_spline->resolution();
+ }
+ return 1;
+}
+
+static void set_spline_resolution(SplinePtr &spline, const int resolution)
+{
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
+ bezier_spline->set_resolution(std::max(resolution, 1));
+ }
+ if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) {
+ nurb_spline->set_resolution(std::max(resolution, 1));
+ }
+}
+
+static GVArrayPtr make_resolution_read_attribute(const CurveEval &curve)
+{
+ return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
+ curve.splines());
+}
+
+static GVMutableArrayPtr make_resolution_write_attribute(CurveEval &curve)
+{
+ return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
+ int,
+ get_spline_resolution,
+ set_spline_resolution>>(
+ curve.splines());
+}
+
+static bool get_cyclic_value(const SplinePtr &spline)
+{
+ return spline->is_cyclic();
+}
+
+static void set_cyclic_value(SplinePtr &spline, const bool value)
+{
+ if (spline->is_cyclic() != value) {
+ spline->set_cyclic(value);
+ spline->mark_cache_invalid();
+ }
+}
+
+static GVArrayPtr make_cyclic_read_attribute(const CurveEval &curve)
+{
+ return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
+ curve.splines());
+}
+
+static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
+ curve.splines());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Builtin Control Point Attributes
+ *
+ * Attributes with a value for every control point. Most of the complexity here is due to the fact
+ * that we must provide access to the attribute data as if it was a contiguous array when it is
+ * really stored separately on each spline. That will be inherently rather slow, but these virtual
+ * array implementations try to make it workable in common situations.
+ * \{ */
+
+template<typename T>
+static void point_attribute_materialize(Span<Span<T>> data,
+ Span<int> offsets,
+ const IndexMask mask,
+ MutableSpan<T> r_span)
+{
+ const int total_size = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : data.index_range()) {
+ const int offset = offsets[spline_index];
+ const int next_offset = offsets[spline_index + 1];
+ r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int dst_index : mask) {
+ while (offsets[spline_index] < dst_index) {
+ spline_index++;
+ }
+
+ const int index_in_spline = dst_index - offsets[spline_index];
+ r_span[dst_index] = data[spline_index][index_in_spline];
+ }
+ }
+}
+
+template<typename T>
+static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
+ Span<int> offsets,
+ const IndexMask mask,
+ MutableSpan<T> r_span)
+{
+ T *dst = r_span.data();
+ const int total_size = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : data.index_range()) {
+ const int offset = offsets[spline_index];
+ const int next_offset = offsets[spline_index + 1];
+ uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int dst_index : mask) {
+ while (offsets[spline_index] < dst_index) {
+ spline_index++;
+ }
+
+ const int index_in_spline = dst_index - offsets[spline_index];
+ new (dst + dst_index) T(data[spline_index][index_in_spline]);
+ }
+ }
+}
+
+/**
+ * Virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VArray_For_SplinePoints : public VArray<T> {
+ private:
+ const Array<Span<T>> data_;
+ Array<int> offsets_;
+
+ public:
+ VArray_For_SplinePoints(Array<Span<T>> data, Array<int> offsets)
+ : VArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return data_[indices.spline_index][indices.point_index];
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize(data_.as_span(), offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize_to_uninitialized(data_.as_span(), offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Mutable virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VMutableArray_For_SplinePoints final : public VMutableArray<T> {
+ private:
+ Array<MutableSpan<T>> data_;
+ Array<int> offsets_;
+
+ public:
+ VMutableArray_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets)
+ : VMutableArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return data_[indices.spline_index][indices.point_index];
+ }
+
+ void set_impl(const int64_t index, T value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ data_[indices.spline_index][indices.point_index] = value;
+ }
+
+ void set_all_impl(Span<T> src) final
+ {
+ for (const int spline_index : data_.index_range()) {
+ const int offset = offsets_[spline_index];
+ const int next_offsets = offsets_[spline_index + 1];
+ data_[spline_index].copy_from(src.slice(offset, next_offsets - offset));
+ }
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize_to_uninitialized(
+ {(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+ }
+};
+
+template<typename T> GVArrayPtr point_data_gvarray(Array<Span<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
+template<typename T>
+GVMutableArrayPtr point_data_gvarray(Array<MutableSpan<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
+/**
+ * Virtual array implementation specifically for control point positions. This is only needed for
+ * Bezier splines, where adjusting the position also requires adjusting handle positions depending
+ * on handle types. We pay a small price for this when other spline types are mixed with Bezier.
+ *
+ * \note There is no need to check the handle type to avoid changing auto handles, since
+ * retrieving write access to the position data will mark them for recomputation anyway.
+ */
+class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
+ private:
+ MutableSpan<SplinePtr> splines_;
+ Array<int> offsets_;
+
+ public:
+ VMutableArray_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets)
+ : VMutableArray<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
+ {
+ }
+
+ float3 get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return splines_[indices.spline_index]->positions()[indices.point_index];
+ }
+
+ void set_impl(const int64_t index, float3 value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ Spline &spline = *splines_[indices.spline_index];
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+ const float3 delta = value - bezier_spline->positions()[indices.point_index];
+ bezier_spline->handle_positions_left()[indices.point_index] += delta;
+ bezier_spline->handle_positions_right()[indices.point_index] += delta;
+ bezier_spline->positions()[indices.point_index] = value;
+ }
+ else {
+ spline.positions()[indices.point_index] = value;
+ }
+ }
+
+ void set_all_impl(Span<float3> src) final
+ {
+ for (const int spline_index : splines_.index_range()) {
+ Spline &spline = *splines_[spline_index];
+ const int offset = offsets_[spline_index];
+ const int next_offset = offsets_[spline_index + 1];
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+ MutableSpan<float3> positions = bezier_spline->positions();
+ MutableSpan<float3> handle_positions_left = bezier_spline->handle_positions_left();
+ MutableSpan<float3> handle_positions_right = bezier_spline->handle_positions_right();
+ for (const int i : IndexRange(next_offset - offset)) {
+ const float3 delta = src[offset + i] - positions[i];
+ handle_positions_left[i] += delta;
+ handle_positions_right[i] += delta;
+ positions[i] = src[offset + i];
+ }
+ }
+ else {
+ spline.positions().copy_from(src.slice(offset, next_offset - offset));
+ }
+ }
+ }
+
+ /** Utility so we can pass positions to the materialize functions above. */
+ Array<Span<float3>> get_position_spans() const
+ {
+ Array<Span<float3>> spans(splines_.size());
+ for (const int i : spans.index_range()) {
+ spans[i] = splines_[i]->positions();
+ }
+ return spans;
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+ {
+ Array<Span<float3>> spans = this->get_position_spans();
+ point_attribute_materialize(spans.as_span(), offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask,
+ MutableSpan<float3> r_span) const final
+ {
+ Array<Span<float3>> spans = this->get_position_spans();
+ point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Provider for any builtin control point attribute that doesn't need
+ * special handling like access to other arrays in the spline.
+ */
+template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttributeProvider {
+ protected:
+ using GetSpan = Span<T> (*)(const Spline &spline);
+ using GetMutableSpan = MutableSpan<T> (*)(Spline &spline);
+ using UpdateOnWrite = void (*)(Spline &spline);
+ const GetSpan get_span_;
+ const GetMutableSpan get_mutable_span_;
+ const UpdateOnWrite update_on_write_;
+
+ public:
+ BuiltinPointAttributeProvider(std::string attribute_name,
+ const WritableEnum writable,
+ const GetSpan get_span,
+ const GetMutableSpan get_mutable_span,
+ const UpdateOnWrite update_on_write)
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ ATTR_DOMAIN_POINT,
+ bke::cpp_type_to_custom_data_type(CPPType::get<T>()),
+ BuiltinAttributeProvider::NonCreatable,
+ writable,
+ BuiltinAttributeProvider::NonDeletable),
+ get_span_(get_span),
+ get_mutable_span_(get_mutable_span),
+ update_on_write_(update_on_write)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const override
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ if (splines.size() == 1) {
+ return std::make_unique<fn::GVArray_For_GSpan>(get_span_(*splines.first()));
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ Array<Span<T>> spans(splines.size());
+ for (const int i : splines.index_range()) {
+ spans[i] = get_span_(*splines[i]);
+ }
+
+ return point_data_gvarray(spans, offsets);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+ if (splines.size() == 1) {
+ return std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
+ get_mutable_span_(*splines.first()));
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ Array<MutableSpan<T>> spans(splines.size());
+ for (const int i : splines.index_range()) {
+ spans[i] = get_mutable_span_(*splines[i]);
+ if (update_on_write_) {
+ update_on_write_(*splines[i]);
+ }
+ }
+
+ return point_data_gvarray(spans, offsets);
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
+ }
+};
+
+/**
+ * Special attribute provider for the position attribute. Keeping this separate means we don't
+ * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the
+ * positions is more clear.
+ */
+class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> {
+ public:
+ PositionAttributeProvider()
+ : BuiltinPointAttributeProvider(
+ "position",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.positions(); },
+ [](Spline &spline) { return spline.positions(); },
+ [](Spline &spline) { spline.mark_cache_invalid(); })
+ {
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ bool curve_has_bezier_spline = false;
+ for (SplinePtr &spline : curve->splines()) {
+ if (spline->type() == Spline::Type::Bezier) {
+ curve_has_bezier_spline = true;
+ break;
+ }
+ }
+
+ /* Use the regular position virtual array when there aren't any Bezier splines
+ * to avoid the overhead of checking the spline type for every point. */
+ if (!curve_has_bezier_spline) {
+ return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
+ }
+
+ /* Changing the positions requires recalculation of cached evaluated data in many cases.
+ * This could set more specific flags in the future to avoid unnecessary recomputation. */
+ for (SplinePtr &spline : curve->splines()) {
+ spline->mark_cache_invalid();
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_SplinePosition>>(
+ offsets.last(), curve->splines(), std::move(offsets));
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Dynamic Control Point Attributes
+ *
+ * The dynamic control point attribute implementation is very similar to the builtin attribute
+ * implementation-- it uses the same virtual array types. In order to work, this code depends on
+ * the fact that all a curve's splines will have the same attributes and they all have the same
+ * type.
+ * \{ */
+
+class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
+ private:
+ static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
+ CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
+
+ public:
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ Vector<GSpan> spans; /* GSpan has no default constructor. */
+ spans.reserve(splines.size());
+ std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_name);
+ if (!first_span) {
+ return {};
+ }
+ spans.append(*first_span);
+ for (const int i : IndexRange(1, splines.size() - 1)) {
+ std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_name);
+ if (!span) {
+ /* All splines should have the same set of data layers. It would be possible to recover
+ * here and return partial data instead, but that would add a lot of complexity for a
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ ReadAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<Span<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ /* This function is almost the same as #try_get_for_read, but without const. */
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+ Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
+ spans.reserve(splines.size());
+ std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_name);
+ if (!first_span) {
+ return {};
+ }
+ spans.append(*first_span);
+ for (const int i : IndexRange(1, splines.size() - 1)) {
+ std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_name);
+ if (!span) {
+ /* All splines should have the same set of data layers. It would be possible to recover
+ * here and return partial data instead, but that would add a lot of complexity for a
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ WriteAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<MutableSpan<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return false;
+ }
+
+ bool layer_freed = false;
+ for (SplinePtr &spline : curve->splines()) {
+ spline->attributes.remove(attribute_name);
+ }
+ return layer_freed;
+ }
+
+ static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
+ const CustomDataType data_type,
+ const int total_size)
+ {
+ switch (initializer.type) {
+ case AttributeInit::Type::Default:
+ /* This function shouldn't be called in this case, since there
+ * is no need to copy anything to the new custom data array. */
+ BLI_assert_unreachable();
+ return {};
+ case AttributeInit::Type::VArray:
+ return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy();
+ case AttributeInit::Type::MoveArray:
+ return std::make_unique<fn::GVArray_For_GSpan>(
+ GSpan(*bke::custom_data_type_to_cpp_type(data_type),
+ static_cast<const AttributeInitMove &>(initializer).data,
+ total_size));
+ }
+ BLI_assert_unreachable();
+ return {};
+ }
+
+ bool try_create(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final
+ {
+ BLI_assert(this->type_is_supported(data_type));
+ if (domain != ATTR_DOMAIN_POINT) {
+ return false;
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+
+ /* First check the one case that allows us to avoid copying the input data. */
+ if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+
+ /* Otherwise just create a custom data layer on each of the splines. */
+ for (const int i : splines.index_range()) {
+ if (!splines[i]->attributes.create(attribute_name, data_type)) {
+ /* If attribute creation fails on one of the splines, we cannot leave the custom data
+ * layers in the previous splines around, so delete them before returning. However,
+ * this is not an expected case. */
+ BLI_assert_unreachable();
+ return false;
+ }
+ }
+
+ /* With a default initializer type, we can keep the values at their initial values. */
+ if (initializer.type == AttributeInit::Type::Default) {
+ return true;
+ }
+
+ WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name);
+ /* We just created the attribute, it should exist. */
+ BLI_assert(write_attribute);
+
+ const int total_size = curve->control_point_offsets().last();
+ GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
+ /* TODO: When we can call a variant of #set_all with a virtual array argument,
+ * this theoretically unnecessary materialize step could be removed. */
+ GVArray_GSpan source_varray_span{*source_varray};
+ write_attribute.varray->set_all(source_varray_span.data());
+
+ if (initializer.type == AttributeInit::Type::MoveArray) {
+ MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
+ }
+
+ return true;
+ }
+
+ bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+
+ /* In a debug build, check that all corresponding custom data layers have the same type. */
+ curve->assert_valid_point_attributes();
+
+ /* Use the first spline as a representative for all the others. */
+ splines.first()->attributes.foreach_attribute(callback, ATTR_DOMAIN_POINT);
+
+ return true;
+ }
+
+ void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
+ {
+ callback(ATTR_DOMAIN_POINT);
+ }
+
+ bool type_is_supported(CustomDataType data_type) const
+ {
+ return ((1ULL << data_type) & supported_types_mask) != 0;
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Provider Declaration
+ * \{ */
+
+/**
+ * In this function all the attribute providers for a curve component are created.
+ * Most data in this function is statically allocated, because it does not change over time.
+ */
+static ComponentAttributeProviders create_attribute_providers_for_curve()
+{
+ static BuiltinSplineAttributeProvider resolution("resolution",
+ CD_PROP_INT32,
+ BuiltinAttributeProvider::Writable,
+ make_resolution_read_attribute,
+ make_resolution_write_attribute);
+
+ static BuiltinSplineAttributeProvider cyclic("cyclic",
+ CD_PROP_BOOL,
+ BuiltinAttributeProvider::Writable,
+ make_cyclic_read_attribute,
+ make_cyclic_write_attribute);
+
+ static CustomDataAccessInfo spline_custom_data_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ nullptr};
+
+ static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
+ spline_custom_data_access);
+
+ static PositionAttributeProvider position;
+
+ static BuiltinPointAttributeProvider<float> radius(
+ "radius",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.radii(); },
+ [](Spline &spline) { return spline.radii(); },
+ nullptr);
+
+ static BuiltinPointAttributeProvider<float> tilt(
+ "tilt",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.tilts(); },
+ [](Spline &spline) { return spline.tilts(); },
+ [](Spline &spline) { spline.mark_cache_invalid(); });
+
+ static DynamicPointAttributeProvider point_custom_data;
+
+ return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic},
+ {&spline_custom_data, &point_custom_data});
+}
+
+} // namespace blender::bke
+
+const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
+{
+ static blender::bke::ComponentAttributeProviders providers =
+ blender::bke::create_attribute_providers_for_curve();
+ return &providers;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
index 68c551645d2..3b1b7456162 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -42,70 +42,116 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_
GeometryComponent *InstancesComponent::copy() const
{
InstancesComponent *new_component = new InstancesComponent();
- new_component->transforms_ = transforms_;
- new_component->instanced_data_ = instanced_data_;
+ new_component->instance_reference_handles_ = instance_reference_handles_;
+ new_component->instance_transforms_ = instance_transforms_;
+ new_component->instance_ids_ = instance_ids_;
+ new_component->references_ = references_;
return new_component;
}
+void InstancesComponent::reserve(int min_capacity)
+{
+ instance_reference_handles_.reserve(min_capacity);
+ instance_transforms_.reserve(min_capacity);
+ instance_ids_.reserve(min_capacity);
+}
+
+/**
+ * Resize the transform, handles, and ID vectors to the specified capacity.
+ *
+ * \note This function should be used carefully, only when it's guaranteed
+ * that the data will be filled.
+ */
+void InstancesComponent::resize(int capacity)
+{
+ instance_reference_handles_.resize(capacity);
+ instance_transforms_.resize(capacity);
+ instance_ids_.resize(capacity);
+}
+
void InstancesComponent::clear()
{
- instanced_data_.clear();
- transforms_.clear();
+ instance_reference_handles_.clear();
+ instance_transforms_.clear();
+ instance_ids_.clear();
+
+ references_.clear();
}
-void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
+void InstancesComponent::add_instance(const int instance_handle,
+ const float4x4 &transform,
+ const int id)
{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_OBJECT;
- data.data.object = object;
- this->add_instance(data, transform, id);
+ BLI_assert(instance_handle >= 0);
+ BLI_assert(instance_handle < references_.size());
+ instance_reference_handles_.append(instance_handle);
+ instance_transforms_.append(transform);
+ instance_ids_.append(id);
}
-void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id)
+blender::Span<int> InstancesComponent::instance_reference_handles() const
{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_COLLECTION;
- data.data.collection = collection;
- this->add_instance(data, transform, id);
+ return instance_reference_handles_;
}
-void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id)
+blender::MutableSpan<int> InstancesComponent::instance_reference_handles()
{
- instanced_data_.append(data);
- transforms_.append(transform);
- ids_.append(id);
+ return instance_reference_handles_;
}
-Span<InstancedData> InstancesComponent::instanced_data() const
+blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms()
+{
+ return instance_transforms_;
+}
+blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const
{
- return instanced_data_;
+ return instance_transforms_;
}
-Span<float4x4> InstancesComponent::transforms() const
+blender::MutableSpan<int> InstancesComponent::instance_ids()
+{
+ return instance_ids_;
+}
+blender::Span<int> InstancesComponent::instance_ids() const
{
- return transforms_;
+ return instance_ids_;
}
-Span<int> InstancesComponent::ids() const
+/**
+ * Returns a handle for the given reference.
+ * If the reference exists already, the handle of the existing reference is returned.
+ * Otherwise a new handle is added.
+ */
+int InstancesComponent::add_reference(InstanceReference reference)
{
- return ids_;
+ return references_.index_of_or_add_as(reference);
}
-MutableSpan<float4x4> InstancesComponent::transforms()
+blender::Span<InstanceReference> InstancesComponent::references() const
{
- return transforms_;
+ return references_;
}
int InstancesComponent::instances_amount() const
{
- const int size = instanced_data_.size();
- BLI_assert(transforms_.size() == size);
- return size;
+ return instance_transforms_.size();
}
bool InstancesComponent::is_empty() const
{
- return transforms_.size() == 0;
+ return this->instance_reference_handles_.size() == 0;
+}
+
+bool InstancesComponent::owns_direct_data() const
+{
+ /* The object and collection instances are not direct data. Instance transforms are direct data
+ * and are always owned. Therefore, instance components always own all their direct data. */
+ return true;
+}
+
+void InstancesComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
}
static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
@@ -164,8 +210,8 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
blender::Span<int> InstancesComponent::almost_unique_ids() const
{
std::lock_guard lock(almost_unique_ids_mutex_);
- if (almost_unique_ids_.size() != ids_.size()) {
- almost_unique_ids_ = generate_unique_instance_ids(ids_);
+ if (almost_unique_ids_.size() != instance_ids_.size()) {
+ almost_unique_ids_ = generate_unique_instance_ids(instance_ids_);
}
return almost_unique_ids_;
}
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index 4fb7bd6a9bd..42f3a854aec 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -32,7 +32,7 @@
/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
-using blender::bke::ReadAttributePtr;
+using blender::fn::GVArray;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
@@ -157,6 +157,20 @@ bool MeshComponent::is_empty() const
return mesh_ == nullptr;
}
+bool MeshComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void MeshComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -187,14 +201,14 @@ namespace blender::bke {
template<typename T>
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int loop_index : IndexRange(mesh.totloop)) {
- const T value = attribute[loop_index];
+ const T value = old_values[loop_index];
const MLoop &loop = mesh.mloop[loop_index];
const int point_index = loop.v;
mixer.mix_in(point_index, value);
@@ -202,55 +216,49 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
/* We compute all interpolated values at once, because for this interpolation, one has to
* iterate over all loops anyway. */
Array<T> values(mesh.totvert);
- adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const int vertex_index = mesh.mloop[loop_index].v;
- r_values[loop_index] = attribute[vertex_index];
+ r_values[loop_index] = old_values[vertex_index];
}
}
-static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
/* It is not strictly necessary to compute the value for all corners here. Instead one could
* lazily lookup the mesh topology when a specific index accessed. This can be more efficient
* when an algorithm only accesses very few of the corner values. However, for the algorithms
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
Array<T> values(mesh.totloop);
- adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
- std::move(values));
+ adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -260,7 +268,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -277,26 +285,23 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -318,26 +323,23 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -356,26 +358,23 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
- adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -387,26 +386,23 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
}
}
-static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
- adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -423,21 +419,18 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -447,7 +440,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -464,21 +457,18 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -488,7 +478,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -503,26 +493,23 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -533,7 +520,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
/* For every corner, mix the values from the adjacent edges on the face. */
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const int loop_index_prev = (loop_index - 1) % poly.totloop;
+ const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop;
const MLoop &loop = mesh.mloop[loop_index];
const MLoop &loop_prev = mesh.mloop[loop_index_prev];
mixer.mix_in(loop_index, old_values[loop.e]);
@@ -544,26 +531,23 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
- adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -579,21 +563,18 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
- adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -603,7 +584,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -620,87 +601,85 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
} // namespace blender::bke
-ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
- const AttributeDomain new_domain) const
+blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
+ blender::fn::GVArrayPtr varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
- if (!attribute) {
+ if (!varray) {
return {};
}
- if (attribute->size() == 0) {
+ if (varray->size() == 0) {
return {};
}
- const AttributeDomain old_domain = attribute->domain();
- if (old_domain == new_domain) {
- return attribute;
+ if (from_domain == to_domain) {
+ return varray;
}
- switch (old_domain) {
+ switch (from_domain) {
case ATTR_DOMAIN_CORNER: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_POINT: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_FACE: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray));
default:
break;
}
@@ -729,28 +708,31 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
namespace blender::bke {
-static float3 get_vertex_position(const MVert &vert)
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size)
{
- return float3(vert.co);
+ return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
+ Span<StructT>((const StructT *)data, domain_size));
}
-static void set_vertex_position(MVert &vert, const float3 &position)
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size)
{
- copy_v3_v3(vert.co, position);
+ return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(
+ MutableSpan<StructT>((StructT *)data, domain_size));
}
-static ReadAttributePtr make_vertex_position_read_attribute(const void *data,
- const int domain_size)
+static float3 get_vertex_position(const MVert &vert)
{
- return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_position>>(
- ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size));
+ return float3(vert.co);
}
-static WriteAttributePtr make_vertex_position_write_attribute(void *data, const int domain_size)
+static void set_vertex_position(MVert &vert, float3 position)
{
- return std::make_unique<
- DerivedArrayWriteAttribute<MVert, float3, get_vertex_position, set_vertex_position>>(
- ATTR_DOMAIN_POINT, MutableSpan<MVert>((MVert *)data, domain_size));
+ copy_v3_v3(vert.co, position);
}
static void tag_normals_dirty_when_writing_position(GeometryComponent &component)
@@ -766,92 +748,45 @@ static int get_material_index(const MPoly &mpoly)
return static_cast<int>(mpoly.mat_nr);
}
-static void set_material_index(MPoly &mpoly, const int &index)
+static void set_material_index(MPoly &mpoly, int index)
{
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
}
-static ReadAttributePtr make_material_index_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MPoly, int, get_material_index>>(
- ATTR_DOMAIN_FACE, Span<MPoly>((const MPoly *)data, domain_size));
-}
-
-static WriteAttributePtr make_material_index_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<
- DerivedArrayWriteAttribute<MPoly, int, get_material_index, set_material_index>>(
- ATTR_DOMAIN_FACE, MutableSpan<MPoly>((MPoly *)data, domain_size));
-}
-
static bool get_shade_smooth(const MPoly &mpoly)
{
return mpoly.flag & ME_SMOOTH;
}
-static void set_shade_smooth(MPoly &mpoly, const bool &value)
+static void set_shade_smooth(MPoly &mpoly, bool value)
{
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
}
-static ReadAttributePtr make_shade_smooth_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MPoly, bool, get_shade_smooth>>(
- ATTR_DOMAIN_FACE, Span<MPoly>((const MPoly *)data, domain_size));
-}
-
-static WriteAttributePtr make_shade_smooth_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<
- DerivedArrayWriteAttribute<MPoly, bool, get_shade_smooth, set_shade_smooth>>(
- ATTR_DOMAIN_FACE, MutableSpan<MPoly>((MPoly *)data, domain_size));
-}
-
static float2 get_loop_uv(const MLoopUV &uv)
{
return float2(uv.uv);
}
-static void set_loop_uv(MLoopUV &uv, const float2 &co)
+static void set_loop_uv(MLoopUV &uv, float2 co)
{
copy_v2_v2(uv.uv, co);
}
-static ReadAttributePtr make_uvs_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, get_loop_uv>>(
- ATTR_DOMAIN_CORNER, Span((const MLoopUV *)data, domain_size));
-}
-
-static WriteAttributePtr make_uvs_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayWriteAttribute<MLoopUV, float2, get_loop_uv, set_loop_uv>>(
- ATTR_DOMAIN_CORNER, MutableSpan((MLoopUV *)data, domain_size));
-}
-
-static Color4f get_loop_color(const MLoopCol &col)
+static ColorGeometry4f get_loop_color(const MLoopCol &col)
{
- Color4f value;
- rgba_uchar_to_float(value, &col.r);
- return value;
+ ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a);
+ ColorGeometry4f linear_color = encoded_color.decode();
+ return linear_color;
}
-static void set_loop_color(MLoopCol &col, const Color4f &value)
+static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color)
{
- rgba_float_to_uchar(&col.r, value);
-}
-
-static ReadAttributePtr make_vertex_color_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MLoopCol, Color4f, get_loop_color>>(
- ATTR_DOMAIN_CORNER, Span((const MLoopCol *)data, domain_size));
-}
-
-static WriteAttributePtr make_vertex_color_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<
- DerivedArrayWriteAttribute<MLoopCol, Color4f, get_loop_color, set_loop_color>>(
- ATTR_DOMAIN_CORNER, MutableSpan((MLoopCol *)data, domain_size));
+ ColorGeometry4b encoded_color = linear_color.encode();
+ col.r = encoded_color.r;
+ col.g = encoded_color.g;
+ col.b = encoded_color.b;
+ col.a = encoded_color.a;
}
static float get_crease(const MEdge &edge)
@@ -859,83 +794,62 @@ static float get_crease(const MEdge &edge)
return edge.crease / 255.0f;
}
-static void set_crease(MEdge &edge, const float &value)
+static void set_crease(MEdge &edge, float value)
{
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
}
-static ReadAttributePtr make_crease_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MEdge, float, get_crease>>(
- ATTR_DOMAIN_EDGE, Span((const MEdge *)data, domain_size));
-}
-
-static WriteAttributePtr make_crease_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayWriteAttribute<MEdge, float, get_crease, set_crease>>(
- ATTR_DOMAIN_EDGE, MutableSpan((MEdge *)data, domain_size));
-}
-
-class VertexWeightWriteAttribute final : public WriteAttribute {
+class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
private:
MDeformVert *dverts_;
const int dvert_index_;
public:
- VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
- : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
+ VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
+ : VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
- void get_internal(const int64_t index, void *r_value) const override
+ float get_impl(const int64_t index) const override
{
- get_internal(dverts_, dvert_index_, index, r_value);
+ return get_internal(dverts_, dvert_index_, index);
}
- void set_internal(const int64_t index, const void *value) override
+ void set_impl(const int64_t index, const float value) override
{
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
- weight->weight = *reinterpret_cast<const float *>(value);
+ weight->weight = value;
}
- static void get_internal(const MDeformVert *dverts,
- const int dvert_index,
- const int64_t index,
- void *r_value)
+ static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index)
{
if (dverts == nullptr) {
- *(float *)r_value = 0.0f;
- return;
+ return 0.0f;
}
const MDeformVert &dvert = dverts[index];
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
if (weight.def_nr == dvert_index) {
- *(float *)r_value = weight.weight;
- return;
+ return weight.weight;
}
}
- *(float *)r_value = 0.0f;
+ return 0.0f;
}
};
-class VertexWeightReadAttribute final : public ReadAttribute {
+class VArray_For_VertexWeights final : public VArray<float> {
private:
const MDeformVert *dverts_;
const int dvert_index_;
public:
- VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
- : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
+ VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index)
+ : VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
- void get_internal(const int64_t index, void *r_value) const override
+ float get_impl(const int64_t index) const override
{
- VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
+ return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index);
}
};
@@ -944,8 +858,8 @@ class VertexWeightReadAttribute final : public ReadAttribute {
*/
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
public:
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
@@ -957,15 +871,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
if (mesh == nullptr || mesh->dvert == nullptr) {
static const float default_value = 0.0f;
- return std::make_unique<ConstantReadAttribute>(
- ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
+ return {std::make_unique<fn::GVArray_For_SingleValueRef>(
+ CPPType::get<float>(), mesh->totvert, &default_value),
+ ATTR_DOMAIN_POINT};
}
- return std::make_unique<VertexWeightReadAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>(
+ mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
+ ATTR_DOMAIN_POINT};
}
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
@@ -986,8 +902,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
&mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
}
- return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ return {
+ std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>(
+ mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
+ ATTR_DOMAIN_POINT};
}
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
@@ -1049,7 +968,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
@@ -1062,8 +981,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
- return std::make_unique<ArrayReadAttribute<float3>>(
- ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly));
+ return std::make_unique<fn::GVArray_For_Span<float3>>(
+ Span<float3>((const float3 *)data, mesh->totpoly));
}
Array<float3> normals(mesh->totpoly);
@@ -1072,10 +991,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
}
- return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
+ return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
}
- WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}
@@ -1085,7 +1004,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
return false;
}
- bool try_create(GeometryComponent &UNUSED(component)) const final
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
@@ -1136,69 +1056,75 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
#undef MAKE_CONST_CUSTOM_DATA_GETTER
#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
- static BuiltinCustomDataLayerProvider position("position",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- CD_MVERT,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- point_access,
- make_vertex_position_read_attribute,
- make_vertex_position_write_attribute,
- tag_normals_dirty_when_writing_position);
+ static BuiltinCustomDataLayerProvider position(
+ "position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_MVERT,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_derived_read_attribute<MVert, float3, get_vertex_position>,
+ make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>,
+ tag_normals_dirty_when_writing_position);
static NormalAttributeProvider normal;
- static BuiltinCustomDataLayerProvider material_index("material_index",
- ATTR_DOMAIN_FACE,
- CD_PROP_INT32,
- CD_MPOLY,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- face_access,
- make_material_index_read_attribute,
- make_material_index_write_attribute,
- nullptr);
-
- static BuiltinCustomDataLayerProvider shade_smooth("shade_smooth",
- ATTR_DOMAIN_FACE,
- CD_PROP_BOOL,
- CD_MPOLY,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- face_access,
- make_shade_smooth_read_attribute,
- make_shade_smooth_write_attribute,
- nullptr);
-
- static BuiltinCustomDataLayerProvider crease("crease",
- ATTR_DOMAIN_EDGE,
- CD_PROP_FLOAT,
- CD_MEDGE,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- edge_access,
- make_crease_read_attribute,
- make_crease_write_attribute,
- nullptr);
-
- static NamedLegacyCustomDataProvider uvs(ATTR_DOMAIN_CORNER,
- CD_PROP_FLOAT2,
- CD_MLOOPUV,
- corner_access,
- make_uvs_read_attribute,
- make_uvs_write_attribute);
-
- static NamedLegacyCustomDataProvider vertex_colors(ATTR_DOMAIN_CORNER,
- CD_PROP_COLOR,
- CD_MLOOPCOL,
- corner_access,
- make_vertex_color_read_attribute,
- make_vertex_color_write_attribute);
+ static BuiltinCustomDataLayerProvider material_index(
+ "material_index",
+ ATTR_DOMAIN_FACE,
+ CD_PROP_INT32,
+ CD_MPOLY,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ face_access,
+ make_derived_read_attribute<MPoly, int, get_material_index>,
+ make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>,
+ nullptr);
+
+ static BuiltinCustomDataLayerProvider shade_smooth(
+ "shade_smooth",
+ ATTR_DOMAIN_FACE,
+ CD_PROP_BOOL,
+ CD_MPOLY,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ face_access,
+ make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
+ make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>,
+ nullptr);
+
+ static BuiltinCustomDataLayerProvider crease(
+ "crease",
+ ATTR_DOMAIN_EDGE,
+ CD_PROP_FLOAT,
+ CD_MEDGE,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ edge_access,
+ make_derived_read_attribute<MEdge, float, get_crease>,
+ make_derived_write_attribute<MEdge, float, get_crease, set_crease>,
+ nullptr);
+
+ static NamedLegacyCustomDataProvider uvs(
+ ATTR_DOMAIN_CORNER,
+ CD_PROP_FLOAT2,
+ CD_MLOOPUV,
+ corner_access,
+ make_derived_read_attribute<MLoopUV, float2, get_loop_uv>,
+ make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>);
+
+ static NamedLegacyCustomDataProvider vertex_colors(
+ ATTR_DOMAIN_CORNER,
+ CD_PROP_COLOR,
+ CD_MLOOPCOL,
+ corner_access,
+ make_derived_read_attribute<MLoopCol, ColorGeometry4f, get_loop_color>,
+ make_derived_write_attribute<MLoopCol, ColorGeometry4f, get_loop_color, set_loop_color>);
static VertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
index 32c4ee8a6a6..6c4af7a6d23 100644
--- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
+++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
@@ -107,6 +107,20 @@ bool PointCloudComponent::is_empty() const
return pointcloud_ == nullptr;
}
+bool PointCloudComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void PointCloudComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -126,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
namespace blender::bke {
-template<typename T, AttributeDomain Domain>
-static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
+template<typename T>
+static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
{
- return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
+ return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
}
-template<typename T, AttributeDomain Domain>
-static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
+template<typename T>
+static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
{
- return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
+ return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
+ MutableSpan<T>((T *)data, domain_size));
}
/**
@@ -165,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
},
update_custom_data_pointers};
- static BuiltinCustomDataLayerProvider position(
- "position",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- CD_PROP_FLOAT3,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- point_access,
- make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
- nullptr);
- static BuiltinCustomDataLayerProvider radius(
- "radius",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT,
- CD_PROP_FLOAT,
- BuiltinAttributeProvider::Creatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::Deletable,
- point_access,
- make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
- nullptr);
+ static BuiltinCustomDataLayerProvider position("position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_array_read_attribute<float3>,
+ make_array_write_attribute<float3>,
+ nullptr);
+ static BuiltinCustomDataLayerProvider radius("radius",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<float>,
+ make_array_write_attribute<float>,
+ nullptr);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
}
diff --git a/source/blender/blenkernel/intern/geometry_component_volume.cc b/source/blender/blenkernel/intern/geometry_component_volume.cc
index fd2327e0bf5..94ed07a63de 100644
--- a/source/blender/blenkernel/intern/geometry_component_volume.cc
+++ b/source/blender/blenkernel/intern/geometry_component_volume.cc
@@ -97,4 +97,18 @@ Volume *VolumeComponent::get_for_write()
return volume_;
}
+bool VolumeComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void VolumeComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ volume_ = BKE_volume_copy_for_eval(volume_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index a09c3f5f3d3..3d85118deee 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -24,6 +24,7 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DNA_collection_types.h"
@@ -49,10 +50,6 @@ GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type)
{
}
-GeometryComponent ::~GeometryComponent()
-{
-}
-
GeometryComponent *GeometryComponent::create(GeometryComponentType component_type)
{
switch (component_type) {
@@ -64,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new InstancesComponent();
case GEO_COMPONENT_TYPE_VOLUME:
return new VolumeComponent();
+ case GEO_COMPONENT_TYPE_CURVE:
+ return new CurveComponent();
}
BLI_assert_unreachable();
return nullptr;
@@ -182,6 +181,15 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (mesh != nullptr) {
BKE_mesh_wrapper_minmax(mesh, *r_min, *r_max);
}
+ const Volume *volume = this->get_volume_for_read();
+ if (volume != nullptr) {
+ BKE_volume_min_max(volume, *r_min, *r_max);
+ }
+ const CurveEval *curve = this->get_curve_for_read();
+ if (curve != nullptr) {
+ /* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */
+ curve->bounds_min_max(*r_min, *r_max, true);
+ }
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
@@ -205,6 +213,25 @@ uint64_t GeometrySet::hash() const
return reinterpret_cast<uint64_t>(this);
}
+/* Remove all geometry components from the geometry set. */
+void GeometrySet::clear()
+{
+ components_.clear();
+}
+
+/* Make sure that the geometry can be cached. This does not ensure ownership of object/collection
+ * instances. */
+void GeometrySet::ensure_owns_direct_data()
+{
+ for (GeometryComponentType type : components_.keys()) {
+ const GeometryComponent *component = this->get_component_for_read(type);
+ if (!component->owns_direct_data()) {
+ GeometryComponent &component_for_write = this->get_component_for_write(type);
+ component_for_write.ensure_owns_direct_data();
+ }
+ }
+}
+
/* Returns a read-only mesh or null. */
const Mesh *GeometrySet::get_mesh_for_read() const
{
@@ -233,6 +260,13 @@ const Volume *GeometrySet::get_volume_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
+/* Returns a read-only curve or null. */
+const CurveEval *GeometrySet::get_curve_for_read() const
+{
+ const CurveComponent *component = this->get_component_for_read<CurveComponent>();
+ return (component == nullptr) ? nullptr : component->get_for_read();
+}
+
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
@@ -254,6 +288,13 @@ bool GeometrySet::has_volume() const
return component != nullptr && component->has_volume();
}
+/* Returns true when the geometry set has a curve component that has a curve. */
+bool GeometrySet::has_curve() const
+{
+ const CurveComponent *component = this->get_component_for_read<CurveComponent>();
+ return component != nullptr && component->has_curve();
+}
+
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -273,6 +314,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
return geometry_set;
}
+/* Create a new geometry set that only contains the given curve. */
+GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ GeometrySet geometry_set;
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ component.replace(curve, ownership);
+ return geometry_set;
+}
+
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -280,6 +330,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
component.replace(mesh, ownership);
}
+/* Clear the existing curve and replace it with the given one. */
+void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ CurveComponent &component = this->get_component_for_write<CurveComponent>();
+ component.replace(curve, ownership);
+}
+
/* Clear the existing point cloud and replace with the given one. */
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
@@ -287,6 +344,13 @@ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipTy
pointcloud_component.replace(pointcloud, ownership);
}
+/* Clear the existing volume and replace with the given one. */
+void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership)
+{
+ VolumeComponent &volume_component = this->get_component_for_write<VolumeComponent>();
+ volume_component.replace(volume, ownership);
+}
+
/* Returns a mutable mesh or null. No ownership is transferred. */
Mesh *GeometrySet::get_mesh_for_write()
{
@@ -308,6 +372,13 @@ Volume *GeometrySet::get_volume_for_write()
return component.get_for_write();
}
+/* Returns a mutable curve or null. No ownership is transferred. */
+CurveEval *GeometrySet::get_curve_for_write()
+{
+ CurveComponent &component = this->get_component_for_write<CurveComponent>();
+ return component.get_for_write();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -324,19 +395,4 @@ bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set)
return geometry_set->get_component_for_read<InstancesComponent>() != nullptr;
}
-int BKE_geometry_set_instances(const GeometrySet *geometry_set,
- float (**r_transforms)[4][4],
- const int **r_almost_unique_ids,
- InstancedData **r_instanced_data)
-{
- const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>();
- if (component == nullptr) {
- return 0;
- }
- *r_transforms = (float(*)[4][4])component->transforms().data();
- *r_instanced_data = (InstancedData *)component->instanced_data().data();
- *r_almost_unique_ids = (const int *)component->almost_unique_ids().data();
- return component->instances_amount();
-}
-
/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 24a402ac545..847df75c8cb 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -15,10 +15,12 @@
*/
#include "BKE_geometry_set_instances.hh"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_collection_types.h"
#include "DNA_mesh_types.h"
@@ -36,30 +38,55 @@ static void geometry_set_collect_recursive_collection(const Collection &collecti
const float4x4 &transform,
Vector<GeometryInstanceGroup> &r_sets);
+static void add_final_mesh_as_geometry_component(const Object &object, GeometrySet &geometry_set)
+{
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(&const_cast<Object &>(object),
+ false);
+
+ if (mesh != nullptr) {
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
+ mesh_component.copy_vertex_group_names_from_object(object);
+ }
+}
+
+static void add_curve_data_as_geometry_component(const Object &object, GeometrySet &geometry_set)
+{
+ BLI_assert(object.type == OB_CURVE);
+ if (object.data != nullptr) {
+ std::unique_ptr<CurveEval> curve = curve_eval_from_dna_curve(*(Curve *)object.data);
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ curve_component.replace(curve.release(), GeometryOwnershipType::Owned);
+ }
+}
+
/**
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
*/
static GeometrySet object_get_geometry_set_for_read(const Object &object)
{
- /* Objects evaluated with a nodes modifier will have a geometry set already. */
+ if (object.type == OB_MESH && object.mode == OB_MODE_EDIT) {
+ GeometrySet geometry_set;
+ if (object.runtime.geometry_set_eval != nullptr) {
+ /* `geometry_set_eval` only contains non-mesh components, see `editbmesh_build_data`. */
+ geometry_set = *object.runtime.geometry_set_eval;
+ }
+ add_final_mesh_as_geometry_component(object, geometry_set);
+ return geometry_set;
+ }
if (object.runtime.geometry_set_eval != nullptr) {
return *object.runtime.geometry_set_eval;
}
/* Otherwise, construct a new geometry set with the component based on the object type. */
- GeometrySet new_geometry_set;
-
+ GeometrySet geometry_set;
if (object.type == OB_MESH) {
- Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(
- &const_cast<Object &>(object), false);
-
- if (mesh != nullptr) {
- BKE_mesh_wrapper_ensure_mdata(mesh);
-
- MeshComponent &mesh_component = new_geometry_set.get_component_for_write<MeshComponent>();
- mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
- mesh_component.copy_vertex_group_names_from_object(object);
- }
+ add_final_mesh_as_geometry_component(object, geometry_set);
+ }
+ else if (object.type == OB_CURVE) {
+ add_curve_data_as_geometry_component(object, geometry_set);
}
/* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the
@@ -68,7 +95,7 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object)
/* TODO: Add volume support. */
/* Return by value since there is not always an existing geometry set owned elsewhere to use. */
- return new_geometry_set;
+ return geometry_set;
}
static void geometry_set_collect_recursive_collection_instance(
@@ -123,21 +150,28 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
const InstancesComponent &instances_component =
*geometry_set.get_component_for_read<InstancesComponent>();
- Span<float4x4> transforms = instances_component.transforms();
- Span<InstancedData> instances = instances_component.instanced_data();
- for (const int i : instances.index_range()) {
- const InstancedData &data = instances[i];
+ Span<float4x4> transforms = instances_component.instance_transforms();
+ Span<int> handles = instances_component.instance_reference_handles();
+ Span<InstanceReference> references = instances_component.references();
+ for (const int i : transforms.index_range()) {
+ const InstanceReference &reference = references[handles[i]];
const float4x4 instance_transform = transform * transforms[i];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- BLI_assert(data.data.object != nullptr);
- const Object &object = *data.data.object;
- geometry_set_collect_recursive_object(object, instance_transform, r_sets);
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- BLI_assert(data.data.collection != nullptr);
- const Collection &collection = *data.data.collection;
- geometry_set_collect_recursive_collection_instance(collection, instance_transform, r_sets);
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
+ geometry_set_collect_recursive_object(object, instance_transform, r_sets);
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ geometry_set_collect_recursive_collection_instance(
+ collection, instance_transform, r_sets);
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
}
}
}
@@ -153,22 +187,140 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
*
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
*/
-Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &geometry_set)
+void geometry_set_gather_instances(const GeometrySet &geometry_set,
+ Vector<GeometryInstanceGroup> &r_instance_groups)
{
- Vector<GeometryInstanceGroup> result_vector;
-
float4x4 unit_transform;
unit_m4(unit_transform.values);
- geometry_set_collect_recursive(geometry_set, unit_transform, result_vector);
+ geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups);
+}
- return result_vector;
+static bool collection_instance_attribute_foreach(const Collection &collection,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count);
+
+static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count);
+
+static bool object_instance_attribute_foreach(const Object &object,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count)
+{
+ GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
+ if (!instances_attribute_foreach_recursive(instance_geometry_set, callback, limit, count)) {
+ return false;
+ }
+
+ if (object.type == OB_EMPTY) {
+ const Collection *collection_instance = object.instance_collection;
+ if (collection_instance != nullptr) {
+ if (!collection_instance_attribute_foreach(*collection_instance, callback, limit, count)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static bool collection_instance_attribute_foreach(const Collection &collection,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count)
+{
+ LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
+ BLI_assert(collection_object->ob != nullptr);
+ const Object &object = *collection_object->ob;
+ if (!object_instance_attribute_foreach(object, callback, limit, count)) {
+ return false;
+ }
+ }
+ LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
+ BLI_assert(collection_child->collection != nullptr);
+ const Collection &collection = *collection_child->collection;
+ if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
+ return false;
+ }
+ }
+ return true;
}
-void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
- Span<GeometryComponentType> component_types,
- Span<GeometryInstanceGroup> set_groups,
- const Set<std::string> &ignored_attributes)
+/**
+ * \return True if the recursive iteration should continue, false if the limit is reached or the
+ * callback has returned false indicating it should stop.
+ */
+static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count)
+{
+ for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
+ if (!component->attribute_foreach(callback)) {
+ return false;
+ }
+ }
+
+ /* Now that this this geometry set is visited, increase the count and check with the limit. */
+ if (limit > 0 && count++ > limit) {
+ return false;
+ }
+
+ const InstancesComponent *instances_component =
+ geometry_set.get_component_for_read<InstancesComponent>();
+ if (instances_component == nullptr) {
+ return true;
+ }
+
+ for (const InstanceReference &reference : instances_component->references()) {
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ const Object &object = reference.object();
+ if (!object_instance_attribute_foreach(object, callback, limit, count)) {
+ return false;
+ }
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ const Collection &collection = reference.collection();
+ if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
+ return false;
+ }
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Call the callback on all of this geometry set's components, including geometry sets from
+ * instances and recursive instances. This is necessary to access available attributes without
+ * making all of the set's geometry real.
+ *
+ * \param limit: The total number of geometry sets to visit before returning early. This is used
+ * to avoid looking through too many geometry sets recursively, as an explicit tradeoff in favor
+ * of performance at the cost of visiting every unique attribute.
+ */
+void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
+ const AttributeForeachCallback callback,
+ const int limit)
+{
+ int count = 0;
+ instances_attribute_foreach_recursive(geometry_set, callback, limit, count);
+}
+
+void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
+ Span<GeometryComponentType> component_types,
+ const Set<std::string> &ignored_attributes,
+ Map<std::string, AttributeKind> &r_attributes)
{
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
@@ -192,7 +344,7 @@ void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
{attribute_kind->data_type, meta_data.data_type});
};
- attributes.add_or_modify(name, add_info, modify_info);
+ r_attributes.add_or_modify(name, add_info, modify_info);
return true;
});
}
@@ -210,6 +362,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
int64_t cd_dirty_poly = 0;
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
+ VectorSet<Material *> materials;
+
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const int tot_transforms = set_group.transforms.size();
@@ -223,6 +377,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
cd_dirty_poly |= mesh.runtime.cd_dirty_poly;
cd_dirty_edge |= mesh.runtime.cd_dirty_edge;
cd_dirty_loop |= mesh.runtime.cd_dirty_loop;
+ for (const int slot_index : IndexRange(mesh.totcol)) {
+ Material *material = mesh.mat[slot_index];
+ materials.add(material);
+ }
}
if (convert_points_to_vertices && set.has_pointcloud()) {
const PointCloud &pointcloud = *set.get_pointcloud_for_read();
@@ -245,6 +403,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
break;
}
}
+ for (const int i : IndexRange(materials.size())) {
+ Material *material = materials[i];
+ BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
+ }
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@@ -258,6 +420,14 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
+
+ Array<int> material_index_map(mesh.totcol);
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *material = mesh.mat[i];
+ const int new_material_index = materials.index_of(material);
+ material_index_map[i] = new_material_index;
+ }
+
for (const float4x4 &transform : set_group.transforms) {
for (const int i : IndexRange(mesh.totvert)) {
const MVert &old_vert = mesh.mvert[i];
@@ -287,6 +457,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
+ if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh.totcol) {
+ new_poly.mat_nr = material_index_map[new_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ new_poly.mat_nr = 0;
+ }
}
vert_offset += mesh.totvert;
@@ -295,6 +472,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
poly_offset += mesh.totpoly;
}
}
+
+ const float3 point_normal{0.0f, 0.0f, 1.0f};
+ short point_normal_short[3];
+ normal_float_to_short_v3(point_normal_short, point_normal);
+
if (convert_points_to_vertices && set.has_pointcloud()) {
const PointCloud &pointcloud = *set.get_pointcloud_for_read();
for (const float4x4 &transform : set_group.transforms) {
@@ -303,6 +485,7 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
const float3 old_position = pointcloud.co[i];
const float3 new_position = transform * old_position;
copy_v3_v3(new_vert.co, new_position);
+ memcpy(&new_vert.no, point_normal_short, sizeof(point_normal_short));
}
vert_offset += pointcloud.totpoint;
}
@@ -324,13 +507,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
BLI_assert(cpp_type != nullptr);
- result.attribute_try_create(entry.key, domain_output, data_type_output);
- WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
- if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
- write_attribute->domain() != domain_output) {
+ result.attribute_try_create(
+ entry.key, domain_output, data_type_output, AttributeInitDefault());
+ WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
+ if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
+ write_attribute.domain != domain_output) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+
+ fn::GVMutableArray_GSpan dst_span{*write_attribute.varray};
int offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -342,11 +527,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
if (domain_size == 0) {
continue; /* Domain size is 0, so no need to increment the offset. */
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
name, domain_output, data_type_output);
if (source_attribute) {
- fn::GSpan src_span = source_attribute->get_span();
+ fn::GVArray_GSpan src_span{*source_attribute};
const void *src_buffer = src_span.data();
for (const int UNUSED(i) : set_group.transforms.index_range()) {
void *dst_buffer = dst_span[offset];
@@ -361,8 +546,48 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
}
}
- write_attribute->apply_span();
+ dst_span.save();
+ }
+}
+
+static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups)
+{
+ Vector<SplinePtr> new_splines;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ if (!set.has_curve()) {
+ continue;
+ }
+
+ const CurveEval &source_curve = *set.get_curve_for_read();
+ for (const SplinePtr &source_spline : source_curve.splines()) {
+ for (const float4x4 &transform : set_group.transforms) {
+ SplinePtr new_spline = source_spline->copy();
+ new_spline->transform(transform);
+ new_splines.append(std::move(new_spline));
+ }
+ }
+ }
+ if (new_splines.is_empty()) {
+ return nullptr;
+ }
+
+ CurveEval *new_curve = new CurveEval();
+ for (SplinePtr &new_spline : new_splines) {
+ new_curve->add_spline(std::move(new_spline));
+ }
+
+ for (SplinePtr &spline : new_curve->splines()) {
+ /* Spline instances should have no custom attributes, since they always come
+ * from original objects which currently do not support custom attributes.
+ *
+ * This is only true as long as a #GeometrySet cannot be instanced directly. */
+ BLI_assert(spline->attributes.data.totlayer == 0);
+ UNUSED_VARS_NDEBUG(spline);
}
+
+ new_curve->attributes.reallocate(new_curve->splines().size());
+ return new_curve;
}
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
@@ -386,10 +611,11 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
/* Don't copy attributes that are stored directly in the mesh data structs. */
Map<std::string, AttributeKind> attributes;
- gather_attribute_info(attributes,
- component_types,
- set_groups,
- {"position", "material_index", "normal", "shade_smooth", "crease"});
+ geometry_set_gather_instances_attribute_info(
+ set_groups,
+ component_types,
+ {"position", "material_index", "normal", "shade_smooth", "crease"},
+ attributes);
join_attributes(
set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component));
}
@@ -413,7 +639,8 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou
PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint);
dst_component.replace(pointcloud);
Map<std::string, AttributeKind> attributes;
- gather_attribute_info(attributes, {GEO_COMPONENT_TYPE_POINT_CLOUD}, set_groups, {});
+ geometry_set_gather_instances_attribute_info(
+ set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {}, attributes);
join_attributes(set_groups,
{GEO_COMPONENT_TYPE_POINT_CLOUD},
attributes,
@@ -425,8 +652,17 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
{
/* Not yet supported. Joining volume grids with the same name requires resampling of at least
* one of the grids. The cell size of the resulting volume has to be determined somehow. */
- VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>();
- UNUSED_VARS(set_groups, dst_component);
+ UNUSED_VARS(set_groups, result);
+}
+
+static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
+{
+ CurveEval *curve = join_curve_splines(set_groups);
+ if (curve == nullptr) {
+ return;
+ }
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ dst_component.replace(curve);
}
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set)
@@ -436,7 +672,8 @@ GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_s
}
GeometrySet new_geometry_set = geometry_set;
- Vector<GeometryInstanceGroup> set_groups = geometry_set_gather_instances(geometry_set);
+ Vector<GeometryInstanceGroup> set_groups;
+ geometry_set_gather_instances(geometry_set, set_groups);
join_instance_groups_mesh(set_groups, true, new_geometry_set);
/* Remove all instances, even though some might contain other non-mesh data. We can't really
* keep only non-mesh instances in general. */
@@ -454,10 +691,12 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
GeometrySet new_geometry_set;
- Vector<GeometryInstanceGroup> set_groups = geometry_set_gather_instances(geometry_set);
+ Vector<GeometryInstanceGroup> set_groups;
+ geometry_set_gather_instances(geometry_set, set_groups);
join_instance_groups_mesh(set_groups, false, new_geometry_set);
join_instance_groups_pointcloud(set_groups, new_geometry_set);
join_instance_groups_volume(set_groups, new_geometry_set);
+ join_instance_groups_curve(set_groups, new_geometry_set);
return new_geometry_set;
}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index f7b895c7ca7..6d1476485ca 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -222,7 +222,7 @@ void BKE_gpencil_blend_read_data(BlendDataReader *reader, bGPdata *gpd)
/* relink palettes (old palettes deprecated, only to convert old files) */
BLO_read_list(reader, &gpd->palettes);
if (gpd->palettes.first != NULL) {
- LISTBASE_FOREACH (Palette *, palette, &gpd->palettes) {
+ LISTBASE_FOREACH (bGPDpalette *, palette, &gpd->palettes) {
BLO_read_list(reader, &palette->colors);
}
}
@@ -653,9 +653,13 @@ bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
* \param gpd: Grease pencil data-block
* \param name: Name of the layer
* \param setactive: Set as active
+ * \param add_to_header: Used to force the layer added at header
* \return Pointer to new layer
*/
-bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
+bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd,
+ const char *name,
+ const bool setactive,
+ const bool add_to_header)
{
bGPDlayer *gpl = NULL;
bGPDlayer *gpl_active = NULL;
@@ -671,14 +675,18 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti
gpl_active = BKE_gpencil_layer_active_get(gpd);
/* Add to data-block. */
- if (gpl_active == NULL) {
- BLI_addtail(&gpd->layers, gpl);
+ if (add_to_header) {
+ BLI_addhead(&gpd->layers, gpl);
}
else {
- /* if active layer, add after that layer */
- BLI_insertlinkafter(&gpd->layers, gpl_active, gpl);
+ if (gpl_active == NULL) {
+ BLI_addtail(&gpd->layers, gpl);
+ }
+ else {
+ /* if active layer, add after that layer */
+ BLI_insertlinkafter(&gpd->layers, gpl_active, gpl);
+ }
}
-
/* annotation vs GP Object behavior is slightly different */
if (gpd->flag & GP_DATA_ANNOTATIONS) {
/* set default color of new strokes for this layer */
@@ -1293,7 +1301,8 @@ bGPDframe *BKE_gpencil_layer_frame_find(bGPDlayer *gpl, int cframe)
return NULL;
}
-/** Get the appropriate gp-frame from a given layer
+/**
+ * Get the appropriate gp-frame from a given layer
* - this sets the layer's actframe var (if allowed to)
* - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
*
@@ -2615,6 +2624,11 @@ static bool gpencil_is_layer_mask(ViewLayer *view_layer, bGPdata *gpd, bGPDlayer
continue;
}
+ /* Skip if masks are disabled for this view layer. */
+ if (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER) {
+ continue;
+ }
+
LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) {
if (STREQ(gpl_mask->info, mask->name)) {
return true;
@@ -2658,6 +2672,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
bGPDframe *act_gpf = gpl->actframe;
bGPDframe *sta_gpf = act_gpf;
bGPDframe *end_gpf = act_gpf ? act_gpf->next : NULL;
+ float prev_opacity = gpl->opacity;
if (gpl->flag & GP_LAYER_HIDE) {
continue;
@@ -2673,9 +2688,12 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
* This is used only in final render and never in Viewport. */
if ((view_layer != NULL) && (gpl->viewlayername[0] != '\0') &&
(!STREQ(view_layer->name, gpl->viewlayername))) {
- /* If the layer is used as mask, cannot be filtered or the masking system
- * will crash because needs the mask layer in the draw pipeline. */
- if (!gpencil_is_layer_mask(view_layer, gpd, gpl)) {
+ /* Do not skip masks when rendering the view-layer so that it can still be used to clip
+ * other layers. Instead set their opacity to zero. */
+ if (gpencil_is_layer_mask(view_layer, gpd, gpl)) {
+ gpl->opacity = 0.0f;
+ }
+ else {
continue;
}
}
@@ -2770,6 +2788,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
if (layer_cb) {
layer_cb(gpl, act_gpf, NULL, thunk);
}
+ gpl->opacity = prev_opacity;
continue;
}
@@ -2807,6 +2826,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
/* If layer solo mode and Paint mode, only keyframes with data are displayed. */
if (GPENCIL_PAINT_MODE(gpd) && (gpl->flag & GP_LAYER_SOLO_MODE) &&
(act_gpf->framenum != cfra)) {
+ gpl->opacity = prev_opacity;
continue;
}
@@ -2817,6 +2837,9 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
stroke_cb(gpl, act_gpf, gps, thunk);
}
}
+
+ /* Restore the opacity in case it was overwritten (used to hide masks in render). */
+ gpl->opacity = prev_opacity;
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 88d3e917a7a..906d0fb0792 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -515,7 +515,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
if (collection != NULL) {
gpl = BKE_gpencil_layer_named_get(gpd, collection->id.name + 2);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true);
+ gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true, false);
}
}
}
@@ -523,7 +523,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
if (gpl == NULL) {
gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 5d8dd99b3ae..7f839650f33 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -530,14 +530,23 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
/**
* Backbone stretch similar to Freestyle.
- * \param gps: Stroke to sample
- * \param dist: Distance of one segment
- * \param tip_length: Ignore tip jittering, set zero to use default value.
+ * \param gps: Stroke to sample.
+ * \param dist: Distance of one segment.
+ * \param overshoot_fac: How exact is the follow curve algorithm.
+ * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End)
*/
-bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
+bool BKE_gpencil_stroke_stretch(bGPDstroke *gps,
+ const float dist,
+ const float overshoot_fac,
+ const short mode)
{
+#define BOTH 0
+#define START 1
+#define END 2
+
bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
- float threshold = (tip_length == 0 ? 0.001f : tip_length);
+ int i;
+ float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac);
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
return false;
@@ -547,33 +556,36 @@ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float t
second_last = &pt[gps->totpoints - 2];
next_pt = &pt[1];
- float len1 = 0.0f;
- float len2 = 0.0f;
-
- int i = 1;
- while (len1 < threshold && gps->totpoints > i) {
- next_pt = &pt[i];
- len1 = len_v3v3(&next_pt->x, &pt->x);
- i++;
- }
+ if (mode == BOTH || mode == START) {
+ float len1 = 0.0f;
+ i = 1;
+ while (len1 < threshold && gps->totpoints > i) {
+ next_pt = &pt[i];
+ len1 = len_v3v3(&next_pt->x, &pt->x);
+ i++;
+ }
+ float extend1 = (len1 + dist) / len1;
+ float result1[3];
- i = 2;
- while (len2 < threshold && gps->totpoints >= i) {
- second_last = &pt[gps->totpoints - i];
- len2 = len_v3v3(&last_pt->x, &second_last->x);
- i++;
+ interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
+ copy_v3_v3(&pt->x, result1);
}
- float extend1 = (len1 + dist) / len1;
- float extend2 = (len2 + dist) / len2;
-
- float result1[3], result2[3];
+ if (mode == BOTH || mode == END) {
+ float len2 = 0.0f;
+ i = 2;
+ while (len2 < threshold && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 = len_v3v3(&last_pt->x, &second_last->x);
+ i++;
+ }
- interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
- interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
+ float extend2 = (len2 + dist) / len2;
+ float result2[3];
+ interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
- copy_v3_v3(&pt->x, result1);
- copy_v3_v3(&last_pt->x, result2);
+ copy_v3_v3(&last_pt->x, result2);
+ }
return true;
}
@@ -702,48 +714,64 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
* Shrink the stroke by length.
* \param gps: Stroke to shrink
* \param dist: delta length
+ * \param mode: 1->Start, 2->End
*/
-bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
+bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mode)
{
+#define START 1
+#define END 2
+
bGPDspoint *pt = gps->points, *second_last;
int i;
- if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+ if (gps->totpoints < 2) {
+ if (gps->totpoints == 1) {
+ second_last = &pt[1];
+ if (len_v3v3(&second_last->x, &pt->x) < dist) {
+ BKE_gpencil_stroke_trim_points(gps, 0, 0);
+ return true;
+ }
+ }
+
return false;
}
second_last = &pt[gps->totpoints - 2];
- float len1, this_len1, cut_len1;
- float len2, this_len2, cut_len2;
- int index_start, index_end;
-
- len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
-
- i = 1;
- while (len1 < dist && gps->totpoints > i - 1) {
- this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
- len1 += this_len1;
- cut_len1 = len1 - dist;
- i++;
+ float len1, cut_len1;
+ float len2, cut_len2;
+ len1 = len2 = cut_len1 = cut_len2 = 0.0f;
+
+ int index_start = 0;
+ int index_end = 0;
+ if (mode == START) {
+ i = 0;
+ index_end = gps->totpoints - 1;
+ while (len1 < dist && gps->totpoints > i + 1) {
+ len1 += len_v3v3(&pt[i].x, &pt[i + 1].x);
+ cut_len1 = len1 - dist;
+ i++;
+ }
+ index_start = i - 1;
}
- index_start = i - 2;
- i = 2;
- while (len2 < dist && gps->totpoints >= i) {
- second_last = &pt[gps->totpoints - i];
- this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
- len2 += this_len2;
- cut_len2 = len2 - dist;
- i++;
+ if (mode == END) {
+ index_start = 0;
+ i = 2;
+ while (len2 < dist && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 += len_v3v3(&second_last[1].x, &second_last->x);
+ cut_len2 = len2 - dist;
+ i++;
+ }
+ index_end = gps->totpoints - i + 2;
}
- index_end = gps->totpoints - i + 2;
- if (len1 < dist || len2 < dist || index_end <= index_start) {
+ if (index_end <= index_start) {
index_start = index_end = 0; /* empty stroke */
}
- if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
+ if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) {
index_start = index_end = 0; /* no length left to cut */
}
@@ -753,22 +781,8 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
return false;
}
- pt = gps->points;
-
- float cut1 = cut_len1 / this_len1;
- float cut2 = cut_len2 / this_len2;
-
- float result1[3], result2[3];
-
- interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
- interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
-
- copy_v3_v3(&pt[0].x, result1);
- copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
-
return true;
}
-
/**
* Apply smooth position to stroke point.
* \param gps: Stroke to smooth
@@ -1050,8 +1064,21 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
normalize_v3(locx);
normalize_v3(locy);
+ /* Calculate last point first. */
+ const bGPDspoint *pt_last = &points[totpoints - 1];
+ float tmp[3];
+ sub_v3_v3v3(tmp, &pt_last->x, &pt0->x);
+
+ points2d[totpoints - 1][0] = dot_v3v3(tmp, locx);
+ points2d[totpoints - 1][1] = dot_v3v3(tmp, locy);
+
+ /* Calculate the scalar cross product of the 2d points. */
+ float cross = 0.0f;
+ float *co_curr;
+ float *co_prev = (float *)&points2d[totpoints - 1];
+
/* Get all points in local space */
- for (int i = 0; i < totpoints; i++) {
+ for (int i = 0; i < totpoints - 1; i++) {
const bGPDspoint *pt = &points[i];
float loc[3];
@@ -1060,10 +1087,15 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
points2d[i][0] = dot_v3v3(loc, locx);
points2d[i][1] = dot_v3v3(loc, locy);
+
+ /* Calculate cross product. */
+ co_curr = (float *)&points2d[i][0];
+ cross += (co_curr[0] - co_prev[0]) * (co_curr[1] + co_prev[1]);
+ co_prev = (float *)&points2d[i][0];
}
- /* Concave (-1), Convex (1), or Auto-detect (0)? */
- *r_direction = (int)locy[2];
+ /* Concave (-1), Convex (1) */
+ *r_direction = (cross >= 0.0f) ? 1 : -1;
}
/**
@@ -2421,7 +2453,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_fill == NULL) {
- gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true);
+ gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
@@ -2433,7 +2465,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
make_element_name(
ob_mesh->id.name + 2, (ma != NULL) ? ma->id.name + 2 : "Fill", 64, element_name);
- mat_idx = gpencil_material_find_index_by_name(ob_gp, element_name);
+ mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
if (mat_idx == -1) {
float color[4];
if (ma != NULL) {
@@ -2474,7 +2506,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_stroke == NULL) {
- gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true);
+ gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
@@ -3663,7 +3695,7 @@ static int generate_perimeter_cap(const float point[4],
/**
* Calculate the perimeter (outline) of a stroke as list of tPerimeterPoint.
- * \param subdivisions: Number of subdivions for the start and end caps
+ * \param subdivisions: Number of subdivisions for the start and end caps
* \return: list of tPerimeterPoint
*/
static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
@@ -3710,7 +3742,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
copy_v3_v3(first_next_pt, &first_next->x);
copy_v3_v3(last_prev_pt, &last_prev->x);
- /* edgecase if single point */
+ /* Edge-case if single point. */
if (gps->totpoints == 1) {
first_next_pt[0] += 1.0f;
last_prev_pt[0] -= 1.0f;
@@ -3784,7 +3816,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
negate_v2(vec_miter_right);
float angle = dot_v2v2(vec_next, nvec_prev);
- /* add two points if angle is close to beeing straight */
+ /* Add two points if angle is close to being straight. */
if (fabsf(angle) < 0.0001f) {
normalize_v2_length(nvec_prev, radius);
normalize_v2_length(nvec_next, radius);
@@ -3910,7 +3942,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
/**
* Calculates the perimeter of a stroke projected from the view and
* returns it as a new stroke.
- * \param subdivisions: Number of subdivions for the start and end caps
+ * \param subdivisions: Number of subdivisions for the start and end caps
* \return: bGPDstroke pointer to stroke perimeter
*/
bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index 6f1896f055a..16386cac029 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -616,7 +616,8 @@ static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob
return remap_cfra;
}
-/** Get the current frame re-timed with time modifiers.
+/**
+ * Get the current frame re-timed with time modifiers.
* \param depsgraph: Current depsgraph.
* \param scene: Current scene
* \param ob: Grease pencil object
@@ -746,7 +747,8 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
BKE_gpencil_update_orig_pointers(ob_orig, ob);
}
-/** Calculate gpencil modifiers.
+/**
+ * Calculate gpencil modifiers.
* \param depsgraph: Current depsgraph
* \param scene: Current scene
* \param ob: Grease pencil object
@@ -755,9 +757,9 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
bGPdata *gpd = (bGPdata *)ob->data;
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
+ const bool is_curve_edit = (bool)(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd) && !is_render);
+ const bool is_multiedit = (bool)(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd) && !is_render);
const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
(ob->greasepencil_modifiers.first != NULL) &&
(!GPENCIL_SIMPLIFY_MODIF(scene)));
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 6b164e6bc50..58715ac2e05 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -503,7 +503,7 @@ void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src)
void IDP_SyncGroupTypes(IDProperty *dest, const IDProperty *src, const bool do_arraylen)
{
- LISTBASE_FOREACH_MUTABLE (IDProperty *, prop_dst, &src->data.group) {
+ LISTBASE_FOREACH_MUTABLE (IDProperty *, prop_dst, &dest->data.group) {
const IDProperty *prop_src = IDP_GetPropertyFromGroup((IDProperty *)src, prop_dst->name);
if (prop_src != NULL) {
/* check of we should replace? */
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 368b1c2e66b..2f7e2b41a73 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -735,6 +735,37 @@ int BKE_image_get_tile_from_pos(struct Image *ima,
return tile_number;
}
+/**
+ * Return the tile_number for the closest UDIM tile.
+ */
+int BKE_image_find_nearest_tile(const Image *image, const float co[2])
+{
+ const float co_floor[2] = {floorf(co[0]), floorf(co[1])};
+ /* Distance to the closest UDIM tile. */
+ float dist_best_sq = FLT_MAX;
+ int tile_number_best = -1;
+
+ LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) {
+ const int tile_index = tile->tile_number - 1001;
+ /* Coordinates of the current tile. */
+ const float tile_index_co[2] = {tile_index % 10, tile_index / 10};
+
+ if (equals_v2v2(co_floor, tile_index_co)) {
+ return tile->tile_number;
+ }
+
+ /* Distance between co[2] and UDIM tile. */
+ const float dist_sq = len_squared_v2v2(tile_index_co, co);
+
+ if (dist_sq < dist_best_sq) {
+ dist_best_sq = dist_sq;
+ tile_number_best = tile->tile_number;
+ }
+ }
+
+ return tile_number_best;
+}
+
static void image_init_color_management(Image *ima)
{
ImBuf *ibuf;
diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c
index ceb13c4955e..1a0cc8c2924 100644
--- a/source/blender/blenkernel/intern/image_gen.c
+++ b/source/blender/blenkernel/intern/image_gen.c
@@ -69,10 +69,11 @@ static void image_buf_fill_color_slice(
}
}
-static void image_buf_fill_color_thread_do(void *data_v, int start_scanline, int num_scanlines)
+static void image_buf_fill_color_thread_do(void *data_v, int scanline)
{
FillColorThreadData *data = (FillColorThreadData *)data_v;
- size_t offset = ((size_t)start_scanline) * data->width * 4;
+ const int num_scanlines = 1;
+ size_t offset = ((size_t)scanline) * data->width * 4;
unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
image_buf_fill_color_slice(rect, rect_float, data->width, num_scanlines, data->color);
@@ -197,13 +198,14 @@ typedef struct FillCheckerThreadData {
int width;
} FillCheckerThreadData;
-static void image_buf_fill_checker_thread_do(void *data_v, int start_scanline, int num_scanlines)
+static void image_buf_fill_checker_thread_do(void *data_v, int scanline)
{
FillCheckerThreadData *data = (FillCheckerThreadData *)data_v;
- size_t offset = ((size_t)start_scanline) * data->width * 4;
+ size_t offset = ((size_t)scanline) * data->width * 4;
+ const int num_scanlines = 1;
unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
- image_buf_fill_checker_slice(rect, rect_float, data->width, num_scanlines, start_scanline);
+ image_buf_fill_checker_slice(rect, rect_float, data->width, num_scanlines, scanline);
}
void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int width, int height)
@@ -444,16 +446,15 @@ typedef struct FillCheckerColorThreadData {
int width, height;
} FillCheckerColorThreadData;
-static void checker_board_color_prepare_thread_do(void *data_v,
- int start_scanline,
- int num_scanlines)
+static void checker_board_color_prepare_thread_do(void *data_v, int scanline)
{
FillCheckerColorThreadData *data = (FillCheckerColorThreadData *)data_v;
- size_t offset = ((size_t)data->width) * start_scanline * 4;
+ const int num_scanlines = 1;
+ size_t offset = ((size_t)data->width) * scanline * 4;
unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
checker_board_color_prepare_slice(
- rect, rect_float, data->width, num_scanlines, start_scanline, data->height);
+ rect, rect_float, data->width, num_scanlines, scanline, data->height);
}
void BKE_image_buf_fill_checker_color(unsigned char *rect,
diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c
index 8847b88d6f2..bb7495437bb 100644
--- a/source/blender/blenkernel/intern/image_gpu.c
+++ b/source/blender/blenkernel/intern/image_gpu.c
@@ -408,17 +408,19 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
store_premultiplied,
limit_gl_texture_size);
- GPU_texture_wrap_mode(*tex, true, false);
+ if (*tex) {
+ GPU_texture_wrap_mode(*tex, true, false);
- if (GPU_mipmap_enabled()) {
- GPU_texture_generate_mipmap(*tex);
- if (ima) {
- ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
+ if (GPU_mipmap_enabled()) {
+ GPU_texture_generate_mipmap(*tex);
+ if (ima) {
+ ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
+ }
+ GPU_texture_mipmap_mode(*tex, true, true);
+ }
+ else {
+ GPU_texture_mipmap_mode(*tex, false, true);
}
- GPU_texture_mipmap_mode(*tex, true, true);
- }
- else {
- GPU_texture_mipmap_mode(*tex, false, true);
}
}
@@ -427,7 +429,9 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
BKE_image_release_ibuf(ima, ibuf_intern, NULL);
}
- GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
+ if (*tex) {
+ GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
+ }
return *tex;
}
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index f2893e162cb..073276b7011 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -105,6 +105,11 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK);
}
+static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id)
+{
+ return ((Key *)id)->from;
+}
+
static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Key *key = (Key *)id;
@@ -216,7 +221,9 @@ IDTypeInfo IDType_ID_KE = {
.make_local = NULL,
.foreach_id = shapekey_foreach_id,
.foreach_cache = NULL,
- .owner_get = NULL, /* Could have one actually? */
+ /* A bit weird, due to shapekeys not being strictly speaking embedded data... But they also
+ * share a lot with those (non linkable, only ever used by one owner ID, etc.). */
+ .owner_get = shapekey_owner_get,
.blend_write = shapekey_blend_write,
.blend_read_data = shapekey_blend_read_data,
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index 7c451051727..1357424d5ff 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -94,6 +94,7 @@ static void lattice_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const i
}
lattice_dst->editlatt = NULL;
+ lattice_dst->batch_cache = NULL;
}
static void lattice_free_data(ID *id)
@@ -540,10 +541,12 @@ void BKE_lattice_vert_coords_apply(Lattice *lt, const float (*vert_coords)[3])
void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Object *ob)
{
+ BKE_object_free_derived_caches(ob);
+ if (ob->runtime.curve_cache == NULL) {
+ ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for lattice");
+ }
+
Lattice *lt = ob->data;
- /* Get vertex coordinates from the original copy;
- * otherwise we get already-modified coordinates. */
- Object *ob_orig = DEG_get_original_object(ob);
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
float(*vert_coords)[3] = NULL;
@@ -551,13 +554,6 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec
const bool is_editmode = (lt->editlatt != NULL);
const ModifierEvalContext mectx = {depsgraph, ob, 0};
- if (ob->runtime.curve_cache) {
- BKE_displist_free(&ob->runtime.curve_cache->disp);
- }
- else {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for lattice");
- }
-
for (; md; md = md->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
@@ -577,49 +573,33 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec
continue;
}
- if (!vert_coords) {
- Lattice *lt_orig = ob_orig->data;
- if (lt_orig->editlatt) {
- lt_orig = lt_orig->editlatt->latt;
- }
- vert_coords = BKE_lattice_vert_coords_alloc(lt_orig, &numVerts);
+ if (vert_coords == NULL) {
+ /* Get either the edit-mode or regular lattice, whichever is in use now. */
+ const Lattice *effective_lattice = BKE_object_get_lattice(ob);
+ vert_coords = BKE_lattice_vert_coords_alloc(effective_lattice, &numVerts);
}
+
mti->deformVerts(md, &mectx, NULL, vert_coords, numVerts);
}
- if (ob->id.tag & LIB_TAG_COPIED_ON_WRITE) {
- if (vert_coords) {
- BKE_lattice_vert_coords_apply(ob->data, vert_coords);
- MEM_freeN(vert_coords);
- }
+ if (vert_coords == NULL) {
+ return;
}
- else {
- /* Displist won't do anything; this is just for posterity's sake until we remove it. */
- if (!vert_coords) {
- Lattice *lt_orig = ob_orig->data;
- if (lt_orig->editlatt) {
- lt_orig = lt_orig->editlatt->latt;
- }
- vert_coords = BKE_lattice_vert_coords_alloc(lt_orig, &numVerts);
- }
-
- DispList *dl = MEM_callocN(sizeof(*dl), "lt_dl");
- dl->type = DL_VERTS;
- dl->parts = 1;
- dl->nr = numVerts;
- dl->verts = (float *)vert_coords;
- BLI_addtail(&ob->runtime.curve_cache->disp, dl);
+ Lattice *lt_eval = BKE_object_get_evaluated_lattice(ob);
+ if (lt_eval == NULL) {
+ BKE_id_copy_ex(NULL, &lt->id, (ID **)&lt_eval, LIB_ID_COPY_LOCALIZE);
+ BKE_object_eval_assign_data(ob, &lt_eval->id, true);
}
+
+ BKE_lattice_vert_coords_apply(lt_eval, vert_coords);
+ MEM_freeN(vert_coords);
}
struct MDeformVert *BKE_lattice_deform_verts_get(const struct Object *oblatt)
{
- Lattice *lt = (Lattice *)oblatt->data;
BLI_assert(oblatt->type == OB_LATTICE);
- if (lt->editlatt) {
- lt = lt->editlatt->latt;
- }
+ Lattice *lt = BKE_object_get_lattice(oblatt);
return lt->dvert;
}
diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c
index 2651042939f..a8126cb5538 100644
--- a/source/blender/blenkernel/intern/lattice_deform.c
+++ b/source/blender/blenkernel/intern/lattice_deform.c
@@ -47,6 +47,7 @@
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "BKE_deform.h"
@@ -69,7 +70,7 @@ typedef struct LatticeDeformData {
LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Object *ob)
{
/* we make an array with all differences */
- Lattice *lt = oblatt->data;
+ Lattice *lt = BKE_object_get_lattice(oblatt);
BPoint *bp;
DispList *dl = oblatt->runtime.curve_cache ?
BKE_displist_find(&oblatt->runtime.curve_cache->disp, DL_VERTS) :
@@ -83,9 +84,6 @@ LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Ob
float latmat[4][4];
LatticeDeformData *lattice_deform_data;
- if (lt->editlatt) {
- lt = lt->editlatt->latt;
- }
bp = lt->def;
const int32_t num_points = lt->pntsu * lt->pntsv * lt->pntsw;
@@ -322,7 +320,9 @@ static void lattice_deform_vert_task(void *__restrict userdata,
lattice_deform_vert_with_dvert(data, index, data->dvert ? &data->dvert[index] : NULL);
}
-static void lattice_vert_task_editmesh(void *__restrict userdata, MempoolIterData *iter)
+static void lattice_vert_task_editmesh(void *__restrict userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
const LatticeDeformUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
@@ -330,7 +330,9 @@ static void lattice_vert_task_editmesh(void *__restrict userdata, MempoolIterDat
lattice_deform_vert_with_dvert(data, BM_elem_index_get(v), dvert);
}
-static void lattice_vert_task_editmesh_no_dvert(void *__restrict userdata, MempoolIterData *iter)
+static void lattice_vert_task_editmesh_no_dvert(void *__restrict userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
const LatticeDeformUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
@@ -399,12 +401,16 @@ static void lattice_deform_coords_impl(const Object *ob_lattice,
* have already been properly set. */
BM_mesh_elem_index_ensure(em_target->bm, BM_VERT);
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+
if (cd_dvert_offset != -1) {
- BLI_task_parallel_mempool(em_target->bm->vpool, &data, lattice_vert_task_editmesh, true);
+ BLI_task_parallel_mempool(
+ em_target->bm->vpool, &data, lattice_vert_task_editmesh, &settings);
}
else {
BLI_task_parallel_mempool(
- em_target->bm->vpool, &data, lattice_vert_task_editmesh_no_dvert, true);
+ em_target->bm->vpool, &data, lattice_vert_task_editmesh_no_dvert, &settings);
}
}
else {
diff --git a/source/blender/blenkernel/intern/lattice_deform_test.cc b/source/blender/blenkernel/intern/lattice_deform_test.cc
index f08d0349598..a7cd5c36ec2 100644
--- a/source/blender/blenkernel/intern/lattice_deform_test.cc
+++ b/source/blender/blenkernel/intern/lattice_deform_test.cc
@@ -51,6 +51,7 @@ static void test_lattice_deform_init(LatticeDeformTestContext *ctx,
ctx->coords[index][2] = (rng->get_float() - 0.5f) * 10;
}
IDType_ID_LT.init_data(&ctx->lattice.id);
+ strcpy(ctx->lattice.id.name, "LTLattice");
IDType_ID_OB.init_data(&ctx->ob_lattice.id);
ctx->ob_lattice.type = OB_LATTICE;
ctx->ob_lattice.data = &ctx->lattice;
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index af921307bfb..f26b85338f0 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -164,7 +164,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id)
id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN);
id->flag &= ~LIB_INDIRECT_WEAK_LINK;
if (id_in_mainlist) {
- if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) {
+ if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) {
bmain->is_memfile_undo_written = false;
}
}
@@ -525,7 +525,13 @@ static int id_copy_libmanagement_cb(LibraryIDLinkCallbackData *cb_data)
/* Increase used IDs refcount if needed and required. */
if ((data->flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0 && (cb_flag & IDWALK_CB_USER)) {
- id_us_plus(id);
+ if ((data->flag & LIB_ID_CREATE_NO_MAIN) != 0) {
+ BLI_assert(cb_data->id_self->tag & LIB_TAG_NO_MAIN);
+ id_us_plus_no_lib(id);
+ }
+ else {
+ id_us_plus(id);
+ }
}
return IDWALK_RET_NOP;
@@ -578,7 +584,7 @@ ID *BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag)
}
}
- /* Early output is source is NULL. */
+ /* Early output if source is NULL. */
if (id == NULL) {
return NULL;
}
@@ -827,7 +833,9 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv)
ListBase *lb = which_libbase(bmain, GS(id->name));
BKE_main_lock(bmain);
BLI_addtail(lb, id);
- BKE_id_new_name_validate(lb, id, NULL);
+ /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new
+ * overrides for recursive resync. */
+ BKE_id_new_name_validate(lb, id, NULL, true);
/* alphabetic insertion: is in new_id */
id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT);
bmain->is_memfile_undo_written = false;
@@ -983,7 +991,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb)
}
for (i = 0; i < lb_len; i++) {
if (!BLI_gset_add(gset, id_array[i]->name + 2)) {
- BKE_id_new_name_validate(lb, id_array[i], NULL);
+ BKE_id_new_name_validate(lb, id_array[i], NULL, false);
}
}
BLI_gset_free(gset, NULL);
@@ -1086,7 +1094,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl
BKE_main_lock(bmain);
BLI_addtail(lb, id);
- BKE_id_new_name_validate(lb, id, name);
+ BKE_id_new_name_validate(lb, id, name, false);
bmain->is_memfile_undo_written = false;
/* alphabetic insertion: is in new_id */
BKE_main_unlock(bmain);
@@ -1215,14 +1223,6 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL);
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0);
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_LOCAL) == 0);
- if (!is_private_id_data) {
- /* When we are handling private ID data, we might still want to manage usercounts, even
- * though that ID data-block is actually outside of Main... */
- BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 ||
- (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0);
- }
- /* Never implicitly copy shapekeys when generating temp data outside of Main database. */
- BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || (flag & LIB_ID_COPY_SHAPEKEY) == 0);
/* 'Private ID' data handling. */
if ((bmain != NULL) && is_private_id_data) {
@@ -1245,6 +1245,13 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
}
BLI_assert(new_id != NULL);
+ if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) != 0) {
+ new_id->tag |= LIB_TAG_COPIED_ON_WRITE;
+ }
+ else {
+ new_id->tag &= ~LIB_TAG_COPIED_ON_WRITE;
+ }
+
const size_t id_len = BKE_libblock_get_alloc_info(GS(new_id->name), NULL);
const size_t id_offset = sizeof(ID);
if ((int)id_len - (int)id_offset > 0) { /* signed to allow neg result */ /* XXX ????? */
@@ -1264,9 +1271,7 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag);
}
- /* We may need our own flag to control that at some point, but for now 'no main' one should be
- * good enough. */
- if ((orig_flag & LIB_ID_CREATE_NO_MAIN) == 0) {
+ if ((orig_flag & LIB_ID_COPY_NO_LIB_OVERRIDE) == 0) {
if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
/* We do not want to copy existing override rules here, as they would break the proper
* remapping between IDs. Proper overrides rules will be re-generated anyway. */
@@ -1350,12 +1355,12 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
BLI_remlink(lb, id);
/* Check if we can actually insert id before or after id_sorting_hint, if given. */
- if (!ELEM(id_sorting_hint, NULL, id)) {
+ if (!ELEM(id_sorting_hint, NULL, id) && id_sorting_hint->lib == id->lib) {
BLI_assert(BLI_findindex(lb, id_sorting_hint) >= 0);
ID *id_sorting_hint_next = id_sorting_hint->next;
if (BLI_strcasecmp(id_sorting_hint->name, id->name) < 0 &&
- (id_sorting_hint_next == NULL ||
+ (id_sorting_hint_next == NULL || id_sorting_hint_next->lib != id->lib ||
BLI_strcasecmp(id_sorting_hint_next->name, id->name) > 0)) {
BLI_insertlinkafter(lb, id_sorting_hint, id);
return;
@@ -1363,7 +1368,7 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
ID *id_sorting_hint_prev = id_sorting_hint->prev;
if (BLI_strcasecmp(id_sorting_hint->name, id->name) > 0 &&
- (id_sorting_hint_prev == NULL ||
+ (id_sorting_hint_prev == NULL || id_sorting_hint_prev->lib != id->lib ||
BLI_strcasecmp(id_sorting_hint_prev->name, id->name) < 0)) {
BLI_insertlinkbefore(lb, id_sorting_hint, id);
return;
@@ -1378,16 +1383,33 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
/* Note: We start from the end, because in typical 'heavy' case (insertion of lots of IDs at
* once using the same base name), newly inserted items will generally be towards the end
* (higher extension numbers). */
- for (idtest = lb->last, item_array_index = ID_SORT_STEP_SIZE - 1; idtest != NULL;
- idtest = idtest->prev, item_array_index--) {
+ bool is_in_library = false;
+ item_array_index = ID_SORT_STEP_SIZE - 1;
+ for (idtest = lb->last; idtest != NULL; idtest = idtest->prev) {
+ if (is_in_library) {
+ if (idtest->lib != id->lib) {
+ /* We got out of expected library 'range' in the list, so we are done here and can move on
+ * to the next step. */
+ break;
+ }
+ }
+ else if (idtest->lib == id->lib) {
+ /* We are entering the expected library 'range' of IDs in the list. */
+ is_in_library = true;
+ }
+
+ if (!is_in_library) {
+ continue;
+ }
+
item_array[item_array_index] = idtest;
if (item_array_index == 0) {
- if ((idtest->lib == NULL && id->lib != NULL) ||
- BLI_strcasecmp(idtest->name, id->name) <= 0) {
+ if (BLI_strcasecmp(idtest->name, id->name) <= 0) {
break;
}
item_array_index = ID_SORT_STEP_SIZE;
}
+ item_array_index--;
}
/* Step two: we go forward in the selected chunk of items and check all of them, as we know
@@ -1399,7 +1421,7 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
* So we can increment that index in any case. */
for (item_array_index++; item_array_index < ID_SORT_STEP_SIZE; item_array_index++) {
idtest = item_array[item_array_index];
- if ((idtest->lib != NULL && id->lib == NULL) || BLI_strcasecmp(idtest->name, id->name) > 0) {
+ if (BLI_strcasecmp(idtest->name, id->name) > 0) {
BLI_insertlinkbefore(lb, idtest, id);
break;
}
@@ -1407,12 +1429,18 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
if (item_array_index == ID_SORT_STEP_SIZE) {
if (idtest == NULL) {
/* If idtest is NULL here, it means that in the first loop, the last comparison was
- * performed exactly on the first item of the list, and that it also failed. In other
- * words, all items in the list are greater than inserted one, so we can put it at the
- * start of the list. */
- /* Note that BLI_insertlinkafter() would have same behavior in that case, but better be
- * explicit here. */
- BLI_addhead(lb, id);
+ * performed exactly on the first item of the list, and that it also failed. And that the
+ * second loop was not walked at all.
+ *
+ * In other words, if `id` is local, all the items in the list are greater than the inserted
+ * one, so we can put it at the start of the list. Or, if `id` is linked, it is the first one
+ * of its library, and we can put it at the very end of the list. */
+ if (ID_IS_LINKED(id)) {
+ BLI_addtail(lb, id);
+ }
+ else {
+ BLI_addhead(lb, id);
+ }
}
else {
BLI_insertlinkafter(lb, idtest, id);
@@ -1531,7 +1559,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
* and that current one is not. */
bool is_valid = false;
for (id_test = lb->first; id_test; id_test = id_test->next) {
- if (id != id_test && !ID_IS_LINKED(id_test)) {
+ if (id != id_test && id_test->lib == id->lib) {
if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) {
/* We expect final_name to not be already used, so this is a failure. */
is_valid = false;
@@ -1587,7 +1615,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
for (id_test = lb->first; id_test; id_test = id_test->next) {
char base_name_test[MAX_ID_NAME - 2];
int number_test;
- if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) &&
+ if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) &&
(ELEM(id_test->name[base_name_len + 2], '.', '\0')) &&
STREQLEN(name, id_test->name + 2, base_name_len) &&
(BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') ==
@@ -1676,16 +1704,21 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
*
* Only for local IDs (linked ones already have a unique ID in their library).
*
+ * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked
+ * (otherwise, just ensure that it is properly sorted).
+ *
* \return true if a new name had to be created.
*/
-bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname)
+bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data)
{
- bool result;
+ bool result = false;
char name[MAX_ID_NAME - 2];
- /* if library, don't rename */
- if (ID_IS_LINKED(id)) {
- return false;
+ /* If library, don't rename (unless explicitly required), but do ensure proper sorting. */
+ if (!do_linked_data && ID_IS_LINKED(id)) {
+ id_sort_by_name(lb, id, NULL);
+
+ return result;
}
/* if no name given, use name of current ID
@@ -1726,7 +1759,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname)
}
/* next to indirect usage in read/writefile also in editobject.c scene.c */
-void BKE_main_id_clear_newpoins(Main *bmain)
+void BKE_main_id_newptr_and_tag_clear(Main *bmain)
{
ID *id;
@@ -2140,7 +2173,7 @@ void BKE_library_make_local(Main *bmain,
TIMEIT_VALUE_PRINT(make_local);
#endif
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BLI_memarena_free(linklist_mem);
#ifdef DEBUG_TIME
@@ -2165,9 +2198,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name)
/* search for id */
idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2);
- if (idtest != NULL) {
+ if (idtest != NULL && !ID_IS_LINKED(idtest)) {
/* BKE_id_new_name_validate also takes care of sorting. */
- BKE_id_new_name_validate(lb, idtest, NULL);
+ BKE_id_new_name_validate(lb, idtest, NULL, false);
bmain->is_memfile_undo_written = false;
}
}
@@ -2177,8 +2210,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name)
*/
void BKE_libblock_rename(Main *bmain, ID *id, const char *name)
{
+ BLI_assert(!ID_IS_LINKED(id));
ListBase *lb = which_libbase(bmain, GS(id->name));
- if (BKE_id_new_name_validate(lb, id, name)) {
+ if (BKE_id_new_name_validate(lb, id, name, false)) {
bmain->is_memfile_undo_written = false;
}
}
diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc
new file mode 100644
index 00000000000..8e21ae88aa6
--- /dev/null
+++ b/source/blender/blenkernel/intern/lib_id_test.cc
@@ -0,0 +1,173 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 by Blender Foundation.
+ */
+#include "testing/testing.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+
+#include "DNA_ID.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+
+namespace blender::bke::tests {
+
+struct LibIDMainSortTestContext {
+ Main *bmain;
+};
+
+static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx)
+{
+ BKE_idtype_init();
+ ctx->bmain = BKE_main_new();
+}
+
+static void test_lib_id_main_sort_free(LibIDMainSortTestContext *ctx)
+{
+ BKE_main_free(ctx->bmain);
+}
+
+static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list)
+{
+ ID *prev_id = nullptr;
+ for (ID *id : list) {
+ EXPECT_EQ(id->prev, prev_id);
+ if (prev_id != nullptr) {
+ EXPECT_EQ(prev_id->next, id);
+ }
+ prev_id = id;
+ }
+ EXPECT_EQ(prev_id->next, nullptr);
+}
+
+TEST(lib_id_main_sort, local_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+ EXPECT_TRUE(ctx.bmain->objects.first == id_a);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_c);
+ test_lib_id_main_sort_check_order({id_a, id_b, id_c});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+TEST(lib_id_main_sort, linked_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A"));
+ Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+
+ id_a->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ id_b->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ id_a->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_a);
+ test_lib_id_main_sort_check_order({id_c, id_b, id_a});
+
+ id_b->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+TEST(lib_id_main_unique_name, local_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+ test_lib_id_main_sort_check_order({id_a, id_b, id_c});
+
+ BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name));
+ BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false);
+ EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0);
+ EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_a);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_a, id_c, id_b});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+TEST(lib_id_main_unique_name, linked_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A"));
+ Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+
+ id_a->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ id_b->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name));
+ BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true);
+ EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0);
+ EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ id_b->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name));
+ BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true);
+ EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 2ee4f1597be..6b2ffa3b944 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -48,6 +48,7 @@
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -80,6 +81,19 @@ static void lib_override_library_property_clear(IDOverrideLibraryProperty *op);
static void lib_override_library_property_operation_clear(
IDOverrideLibraryPropertyOperation *opop);
+/** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */
+BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id)
+{
+ if (id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) {
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->owner_get != NULL) {
+ return id_type->owner_get(bmain, id)->override_library;
+ }
+ BLI_assert(!"IDTypeInfo of liboverride-embedded ID with no owner getter");
+ }
+ return id->override_library;
+}
+
/** Initialize empty overriding of \a reference_id by \a local_id. */
IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
{
@@ -118,7 +132,7 @@ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
/** Shalow or deep copy of a whole override from \a src_id to \a dst_id. */
void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_full_copy)
{
- BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id) || ID_IS_OVERRIDE_LIBRARY_TEMPLATE(src_id));
if (dst_id->override_library != NULL) {
if (src_id->override_library == NULL) {
@@ -194,9 +208,17 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo
*override = NULL;
}
-static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
+static ID *lib_override_library_create_from(Main *bmain,
+ ID *reference_id,
+ const int lib_id_copy_flags)
{
- ID *local_id = BKE_id_copy(bmain, reference_id);
+ /* Note: We do not want to copy possible override data from reference here (whether it is an
+ * override template, or already an override of some other ref data). */
+ ID *local_id = BKE_id_copy_ex(bmain,
+ reference_id,
+ NULL,
+ LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE |
+ lib_id_copy_flags);
if (local_id == NULL) {
return NULL;
@@ -218,16 +240,25 @@ static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
return local_id;
}
-/** Check if given ID has some override rules that actually indicate the user edited it.
+/**
+ * Check if given ID has some override rules that actually indicate the user edited it.
*
- * TODO: This could be simplified by storing a flag in IDOverrideLibrary during the diffing
- * process? */
+ * TODO: This could be simplified by storing a flag in #IDOverrideLibrary during the diffing
+ * process?
+ */
bool BKE_lib_override_library_is_user_edited(struct ID *id)
{
if (!ID_IS_OVERRIDE_LIBRARY(id)) {
return false;
}
+ /* A bit weird, but those embedded IDs are handled by their owner ID anyway, so we can just
+ * assume they are never user-edited, actual proper detection will happen from their owner check.
+ */
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ return false;
+ }
+
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
@@ -252,7 +283,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
BLI_assert(reference_id != NULL);
BLI_assert(reference_id->lib != NULL);
- ID *local_id = lib_override_library_create_from(bmain, reference_id);
+ ID *local_id = lib_override_library_create_from(bmain, reference_id, 0);
if (do_tagged_remap) {
Key *reference_key, *local_key = NULL;
@@ -297,9 +328,17 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
* main. You can add more local IDs to be remapped to use new overriding ones by setting their
* LIB_TAG_DOIT tag.
*
+ * \param reference_library the library from which the linked data being overridden come from
+ * (i.e. the library of the linked reference ID).
+ *
+ * \param do_no_main Create the new override data outside of Main database. Used for resyncing of
+ * linked overrides.
+ *
* \return \a true on success, \a false otherwise.
*/
-bool BKE_lib_override_library_create_from_tag(Main *bmain)
+bool BKE_lib_override_library_create_from_tag(Main *bmain,
+ const Library *reference_library,
+ const bool do_no_main)
{
ID *reference_id;
bool success = true;
@@ -309,7 +348,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Get all IDs we want to override. */
FOREACH_MAIN_ID_BEGIN (bmain, reference_id) {
- if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL &&
+ if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib == reference_library &&
BKE_idtype_idcode_is_linkable(GS(reference_id->name))) {
todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__);
todo_id_iter->data = reference_id;
@@ -321,10 +360,16 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Override the IDs. */
for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
reference_id = todo_id_iter->data;
+
+ /* If `newid` is already set, assume it has been handled by calling code.
+ * Only current use case: re-using proxy ID when converting to liboverride. */
if (reference_id->newid == NULL) {
- /* If `newid` is already set, assume it has been handled by calling code.
- * Only current use case: re-using proxy ID when converting to liboverride. */
- if ((reference_id->newid = lib_override_library_create_from(bmain, reference_id)) == NULL) {
+ /* Note: `no main` case is used during resync procedure, to support recursive resync.
+ * This requires extra care further down the resync process,
+ * see: #BKE_lib_override_library_resync. */
+ reference_id->newid = lib_override_library_create_from(
+ bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
+ if (reference_id->newid == NULL) {
success = false;
break;
}
@@ -364,23 +409,43 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Still checking the whole Main, that way we can tag other local IDs as needing to be
* remapped to use newly created overriding IDs, if needed. */
- FOREACH_MAIN_ID_BEGIN (bmain, other_id) {
- if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) {
- /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap
- * local IDs usages anyway. */
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ /* In case we created new overrides as 'no main', they are not accessible directly in this
+ * loop, but we can get to them through their reference's `newid` pointer. */
+ if (do_no_main && id->lib == reference_id->lib && id->newid != NULL) {
+ other_id = id->newid;
+ /* Otherwise we cannot properly distinguish between IDs that are actually from the
+ * linked library (and should not be remapped), and IDs that are overrides re-generated
+ * from the reference from the linked library, and must therefore be remapped.
+ *
+ * This is reset afterwards at the end of this loop. */
+ other_id->lib = NULL;
+ }
+ else {
+ other_id = id;
+ }
+
+ /* If other ID is a linked one, but not from the same library as our reference, then we
+ * consider we should also remap it, as part of recursive resync. */
+ if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib &&
+ other_id != local_id) {
BKE_libblock_relink_ex(bmain,
other_id,
reference_id,
local_id,
- ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
if (reference_key != NULL) {
BKE_libblock_relink_ex(bmain,
other_id,
&reference_key->id,
&local_key->id,
- ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
}
}
+ if (other_id != id) {
+ other_id->lib = reference_id->lib;
+ }
}
FOREACH_MAIN_ID_END;
}
@@ -404,6 +469,8 @@ typedef struct LibOverrideGroupTagData {
ID *id_root;
uint tag;
uint missing_tag;
+ /* Whether we are looping on override data, or their references (linked) one. */
+ bool is_override;
} LibOverrideGroupTagData;
/* Tag all IDs in dependency relationships within an override hierarchy/group.
@@ -416,6 +483,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
{
Main *bmain = data->bmain;
ID *id = data->id_root;
+ const bool is_override = data->is_override;
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
BLI_assert(entry != NULL);
@@ -430,19 +498,23 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
to_id_entry = to_id_entry->next) {
- if ((to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) {
- /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as
* actual dependencies. */
continue;
}
/* We only consider IDs from the same library. */
ID *to_id = *to_id_entry->id_pointer.to;
- if (to_id != NULL && to_id->lib == id->lib) {
- LibOverrideGroupTagData sub_data = *data;
- sub_data.id_root = to_id;
- if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) {
- id->tag |= data->tag;
- }
+ if (to_id == NULL || to_id->lib != id->lib ||
+ (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) {
+ /* IDs from different libraries, or non-override IDs in case we are processing overrides, are
+ * both barriers of dependency. */
+ continue;
+ }
+ LibOverrideGroupTagData sub_data = *data;
+ sub_data.id_root = to_id;
+ if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) {
+ id->tag |= data->tag;
}
}
@@ -454,6 +526,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
Main *bmain = data->bmain;
ID *id_owner = data->id_root;
BLI_assert(ID_IS_LINKED(id_owner));
+ BLI_assert(!data->is_override);
const uint tag = data->tag;
const uint missing_tag = data->missing_tag;
@@ -471,10 +544,8 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
to_id_entry = to_id_entry->next) {
- if ((to_id_entry->usage_flag &
- (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) != 0) {
- /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers), nor
- * override references or embedded ID pointers, as actual dependencies. */
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
continue;
}
@@ -521,6 +592,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
ID *id_root = data->id_root;
+ BLI_assert(!data->is_override);
if ((id_root->tag & LIB_TAG_MISSING)) {
id_root->tag |= data->missing_tag;
@@ -547,11 +619,12 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
}
}
-static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data)
+static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
ID *id_owner = data->id_root;
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner));
+ BLI_assert(data->is_override);
const uint tag = data->tag;
const uint missing_tag = data->missing_tag;
@@ -569,10 +642,8 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
to_id_entry = to_id_entry->next) {
- if ((to_id_entry->usage_flag &
- (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) != 0) {
- /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers), nor
- * override references or embedded ID pointers, as actual dependencies. */
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
continue;
}
@@ -580,45 +651,37 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data
if (ELEM(to_id, NULL, id_owner)) {
continue;
}
- if (!ID_IS_OVERRIDE_LIBRARY(to_id) || ID_IS_LINKED(to_id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id_owner->lib)) {
continue;
}
- /* Do not tag 'virtual' overrides (shape keys here, as we already rejected embedded case
- * above). */
- if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id)) {
- Library *reference_lib = NULL;
- if (GS(id_owner->name) == ID_KE) {
- reference_lib = ((Key *)id_owner)->from->override_library->reference->lib;
- }
- else {
- reference_lib = id_owner->override_library->reference->lib;
- }
- if (to_id->override_library->reference->lib != reference_lib) {
- /* We do not override data-blocks from other libraries, nor do we process them. */
- continue;
- }
+ Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib;
+ ID *to_id_reference = lib_override_get(bmain, to_id)->reference;
+ if (to_id_reference->lib != reference_lib) {
+ /* We do not override data-blocks from other libraries, nor do we process them. */
+ continue;
+ }
- if (to_id->override_library->reference->tag & LIB_TAG_MISSING) {
- to_id->tag |= missing_tag;
- }
- else {
- to_id->tag |= tag;
- }
+ if (to_id_reference->tag & LIB_TAG_MISSING) {
+ to_id->tag |= missing_tag;
+ }
+ else {
+ to_id->tag |= tag;
}
/* Recursively process the dependencies. */
LibOverrideGroupTagData sub_data = *data;
sub_data.id_root = to_id;
- lib_override_local_group_tag_recursive(&sub_data);
+ lib_override_overrides_group_tag_recursive(&sub_data);
}
}
/* This will tag all override IDs of an override group defined by the given `id_root`. */
-static void lib_override_local_group_tag(LibOverrideGroupTagData *data)
+static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
{
ID *id_root = data->id_root;
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root) && !ID_IS_LINKED(id_root));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
+ BLI_assert(data->is_override);
if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) {
id_root->tag |= data->missing_tag;
@@ -628,14 +691,17 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data)
}
/* Tag all local overrides in id_root's group. */
- lib_override_local_group_tag_recursive(data);
+ lib_override_overrides_group_tag_recursive(data);
}
static bool lib_override_library_create_do(Main *bmain, ID *id_root)
{
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = false};
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@@ -643,19 +709,7 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root)
BKE_main_relations_free(bmain);
- return BKE_lib_override_library_create_from_tag(bmain);
-}
-
-BLI_INLINE bool lib_override_library_create_post_process_object_is_instantiated(
- ViewLayer *view_layer, Object *object, const bool is_resync)
-{
- /* We cannot rely on check for object being actually instantiated in resync case, because often
- * the overridden collection is 'excluded' from the current view-layer.
- *
- * Fallback to a basic user-count check then, this is weak (since it could lead to some object
- * not being instantiated at all), but it should work fine in most common cases. */
- return ((is_resync && ID_REAL_USERS(object) >= 1) ||
- (!is_resync && BKE_view_layer_base_find(view_layer, object) != NULL));
+ return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false);
}
static void lib_override_library_create_post_process(Main *bmain,
@@ -666,15 +720,28 @@ static void lib_override_library_create_post_process(Main *bmain,
Collection *residual_storage,
const bool is_resync)
{
+ /* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we
+ * do not do anything about it. */
+
BKE_main_collection_sync(bmain);
- if (id_root->newid != NULL) {
+ /* We create a set of all objects referenced into the scene by its hierarchy of collections.
+ * NOTE: This is different that the list of bases, since objects in excluded collections etc.
+ * won't have a base, but are still considered as instanced from our point of view. */
+ GSet *all_objects_in_scene = BKE_scene_objects_as_gset(scene, NULL);
+
+ /* Instantiating the root collection or object should never be needed in resync case, since the
+ * old override would be remapped to the new one. */
+ if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) {
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
(Object *)id_reference :
NULL;
Collection *collection_new = ((Collection *)id_root->newid);
+ if (is_resync && BKE_collection_is_in_scene(collection_new)) {
+ break;
+ }
if (ob_reference != NULL) {
BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
}
@@ -688,43 +755,16 @@ static void lib_override_library_create_post_process(Main *bmain,
bmain, scene, ((Collection *)id_root), collection_new);
}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
- if (ob_new != NULL && ob_new->id.override_library != NULL) {
- if (ob_reference != NULL) {
- Base *base = BKE_view_layer_base_find(view_layer, ob_new);
- if (!lib_override_library_create_post_process_object_is_instantiated(
- view_layer, ob_new, is_resync)) {
- BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new);
- base = BKE_view_layer_base_find(view_layer, ob_new);
- DEG_id_tag_update_ex(
- bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
- }
+ BLI_assert(BKE_collection_is_in_scene(collection_new));
- if (ob_new == (Object *)ob_reference->id.newid && base != NULL) {
- /* TODO: is setting active needed? */
- BKE_view_layer_base_select_and_set_active(view_layer, base);
- }
- }
- else if (!lib_override_library_create_post_process_object_is_instantiated(
- view_layer, ob_new, is_resync)) {
- BKE_collection_object_add(bmain, collection_new, ob_new);
- DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
- }
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
break;
}
case ID_OB: {
Object *ob_new = (Object *)id_root->newid;
- if (!lib_override_library_create_post_process_object_is_instantiated(
- view_layer, ob_new, is_resync)) {
- if (is_resync && residual_storage != NULL) {
- BKE_collection_object_add(bmain, residual_storage, ob_new);
- }
- else {
- BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
- }
+ if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
+ BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
+ all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
}
break;
}
@@ -734,57 +774,71 @@ static void lib_override_library_create_post_process(Main *bmain,
}
/* We need to ensure all new overrides of objects are properly instantiated. */
+ Collection *default_instantiating_collection = residual_storage;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
Object *ob_new = (Object *)ob->id.newid;
- if (ob_new != NULL) {
- BLI_assert(ob_new->id.override_library != NULL &&
- ob_new->id.override_library->reference == &ob->id);
-
- Collection *default_instantiating_collection = residual_storage;
- if (!lib_override_library_create_post_process_object_is_instantiated(
- view_layer, ob_new, is_resync)) {
- if (default_instantiating_collection == NULL) {
- switch (GS(id_root->name)) {
- case ID_GR: {
- default_instantiating_collection = BKE_collection_add(
- bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
- /* Hide the collection from viewport and render. */
- default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
- break;
+ if (ob_new == NULL || ob_new->id.lib != NULL) {
+ continue;
+ }
+
+ BLI_assert(ob_new->id.override_library != NULL &&
+ ob_new->id.override_library->reference == &ob->id);
+
+ if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
+ if (id_root != NULL && default_instantiating_collection == NULL) {
+ ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root;
+ switch (GS(id_ref->name)) {
+ case ID_GR: {
+ /* Adding the object to a specific collection outside of the root overridden one is a
+ * fairly bad idea (it breaks the override hierarchy concept). But there is no other
+ * way to do this currently (we cannot add new collections to overridden root one,
+ * this is not currently supported).
+ * Since that will be fairly annoying and noisy, only do that in case the override
+ * object is not part of any existing collection (i.e. its user count is 0). In
+ * practice this should never happen I think. */
+ if (ID_REAL_USERS(ob_new) != 0) {
+ continue;
}
- case ID_OB: {
- /* Add the other objects to one of the collections instantiating the
- * root object, or scene's master collection if none found. */
- Object *ob_root = (Object *)id_root;
- LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
- if (BKE_collection_has_object(collection, ob_root) &&
- BKE_view_layer_has_collection(view_layer, collection) &&
- !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
- default_instantiating_collection = collection;
- }
- }
- if (default_instantiating_collection == NULL) {
- default_instantiating_collection = scene->master_collection;
+ default_instantiating_collection = BKE_collection_add(
+ bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
+ /* Hide the collection from viewport and render. */
+ default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
+ COLLECTION_RESTRICT_RENDER;
+ break;
+ }
+ case ID_OB: {
+ /* Add the other objects to one of the collections instantiating the
+ * root object, or scene's master collection if none found. */
+ Object *ob_ref = (Object *)id_ref;
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_has_object(collection, ob_ref) &&
+ BKE_view_layer_has_collection(view_layer, collection) &&
+ !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
+ default_instantiating_collection = collection;
}
- break;
}
- default:
- BLI_assert(0);
+ break;
}
+ default:
+ BLI_assert(0);
}
-
- BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
- DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
+ if (default_instantiating_collection == NULL) {
+ default_instantiating_collection = scene->master_collection;
+ }
+
+ BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
}
+
+ BLI_gset_free(all_objects_in_scene, NULL);
}
/**
* Advanced 'smart' function to create fully functional overrides.
*
- * \note Currently it only does special things if given \a id_root is an object of collection, more
+ * \note Currently it only does special things if given \a id_root is an object or collection, more
* specific behaviors may be added in the future for other ID types.
*
* \note It will override all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at
@@ -794,22 +848,35 @@ static void lib_override_library_create_post_process(Main *bmain,
* \param id_reference: Some reference ID used to do some post-processing after overrides have been
* created, may be NULL. Typically, the Empty object instantiating the linked collection we
* override, currently.
+ * \param r_id_root_override if not NULL, the override generated for the given \a id_root.
* \return true if override was successfully created.
*/
-bool BKE_lib_override_library_create(
- Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference)
+bool BKE_lib_override_library_create(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ID *id_root,
+ ID *id_reference,
+ ID **r_id_root_override)
{
+ if (r_id_root_override != NULL) {
+ *r_id_root_override = NULL;
+ }
+
const bool success = lib_override_library_create_do(bmain, id_root);
if (!success) {
return success;
}
+ if (r_id_root_override != NULL) {
+ *r_id_root_override = id_root->newid;
+ }
+
lib_override_library_create_post_process(
bmain, scene, view_layer, id_root, id_reference, NULL, false);
/* Cleanup. */
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
/* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
@@ -819,6 +886,22 @@ bool BKE_lib_override_library_create(
}
/**
+ * Create a library override template.
+ */
+bool BKE_lib_override_library_template_create(struct ID *id)
+{
+ if (ID_IS_LINKED(id)) {
+ return false;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ return false;
+ }
+
+ BKE_lib_override_library_init(id, NULL);
+ return true;
+}
+
+/**
* Convert a given proxy object into a library override.
*
* \note This is a thin wrapper around \a BKE_lib_override_library_create, only extra work is to
@@ -858,7 +941,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE);
- return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference);
+ return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL);
}
/**
@@ -873,20 +956,25 @@ bool BKE_lib_override_library_resync(Main *bmain,
ViewLayer *view_layer,
ID *id_root,
Collection *override_resync_residual_storage,
- const bool do_hierarchy_enforce)
+ const bool do_hierarchy_enforce,
+ const bool do_post_process,
+ ReportList *reports)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
- BLI_assert(!ID_IS_LINKED(id_root));
ID *id_root_reference = id_root->override_library->reference;
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
- lib_override_local_group_tag(&data);
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = true};
+ lib_override_overrides_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
data.id_root = id_root_reference;
+ data.is_override = false;
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@@ -897,12 +985,48 @@ bool BKE_lib_override_library_resync(Main *bmain,
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (id->tag & LIB_TAG_DOIT && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ /* IDs that get fully removed from linked data remain as local overrides (using place-holder
+ * linked IDs as reference), but they are often not reachable from any current valid local
+ * override hierarchy anymore. This will ensure they get properly deleted at the end of this
+ * function. */
+ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ (id->override_library->reference->tag & LIB_TAG_MISSING) != 0 &&
+ /* Unfortunately deleting obdata means deleting their objects too. Since there is no
+ * guarantee that a valid override object using an obsolete override obdata gets properly
+ * updated, we ignore those here for now. In practice this should not be a big issue. */
+ !OB_DATA_SUPPORT_ID(GS(id->name))) {
+ id->tag |= LIB_TAG_MISSING;
+ }
+
+ if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) {
/* While this should not happen in typical cases (and won't be properly supported here), user
* is free to do all kind of very bad things, including having different local overrides of a
* same linked ID in a same hierarchy. */
- if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) {
- BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id);
+ IDOverrideLibrary *id_override_library = lib_override_get(bmain, id);
+ ID *reference_id = id_override_library->reference;
+ if (GS(reference_id->name) != GS(id->name)) {
+ switch (GS(id->name)) {
+ case ID_KE:
+ reference_id = (ID *)BKE_key_from_id(reference_id);
+ break;
+ case ID_GR:
+ BLI_assert(GS(reference_id->name) == ID_SCE);
+ reference_id = (ID *)((Scene *)reference_id)->master_collection;
+ break;
+ case ID_NT:
+ reference_id = (ID *)ntreeFromID(id);
+ break;
+ default:
+ break;
+ }
+ }
+ BLI_assert(GS(reference_id->name) == GS(id->name));
+
+ if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) {
+ BLI_ghash_insert(linkedref_to_old_override, reference_id, id);
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) {
/* We have an override, but now it does not seem to be necessary to override that ID
* anymore. Check if there are some actual overrides from the user, otherwise assume
@@ -941,7 +1065,8 @@ bool BKE_lib_override_library_resync(Main *bmain,
/* Note that this call also remaps all pointers of tagged IDs from old override IDs to new
* override IDs (including within the old overrides themselves, since those are tagged too
* above). */
- const bool success = BKE_lib_override_library_create_from_tag(bmain);
+ const bool success = BKE_lib_override_library_create_from_tag(
+ bmain, id_root_reference->lib, true);
if (!success) {
return success;
@@ -950,55 +1075,104 @@ bool BKE_lib_override_library_resync(Main *bmain,
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
- if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
+ /* We need to 'move back' newly created override into its proper library (since it was
+ * duplicated from the reference ID with 'no main' option, it should currently be the same
+ * as the reference ID one). */
+ BLI_assert(/*id_override_new->lib == NULL || */ id_override_new->lib == id->lib);
+ BLI_assert(id_override_old == NULL || id_override_old->lib == id_root->lib);
+ id_override_new->lib = id_root->lib;
+ /* Remap step below will tag directly linked ones properly as needed. */
+ if (ID_IS_LINKED(id_override_new)) {
+ id_override_new->tag |= LIB_TAG_INDIRECT;
+ }
+
if (id_override_old != NULL) {
/* Swap the names between old override ID and new one. */
char id_name_buf[MAX_ID_NAME];
memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf));
memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name));
memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name));
- /* Note that this is a very efficient way to keep BMain IDs ordered as expected after
- * swapping their names.
- * However, one has to be very careful with this when iterating over the listbase at the
- * same time. Here it works because we only execute this code when we are in the linked
- * IDs, which are always *after* all local ones, and we only affect local IDs. */
- BLI_listbase_swaplinks(lb, id_override_old, id_override_new);
-
- /* Remap the whole local IDs to use the new override. */
- BKE_libblock_remap(
- bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE);
-
- /* Copy over overrides rules from old override ID to new one. */
- BLI_duplicatelist(&id_override_new->override_library->properties,
- &id_override_old->override_library->properties);
- for (IDOverrideLibraryProperty *
- op_new = id_override_new->override_library->properties.first,
- *op_old = id_override_old->override_library->properties.first;
- op_new;
- op_new = op_new->next, op_old = op_old->next) {
- lib_override_library_property_copy(op_new, op_old);
+
+ BLI_insertlinkreplace(lb, id_override_old, id_override_new);
+ id_override_old->tag |= LIB_TAG_NO_MAIN;
+ id_override_new->tag &= ~LIB_TAG_NO_MAIN;
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old));
+
+ /* Copy over overrides rules from old override ID to new one. */
+ BLI_duplicatelist(&id_override_new->override_library->properties,
+ &id_override_old->override_library->properties);
+ IDOverrideLibraryProperty *op_new =
+ id_override_new->override_library->properties.first;
+ IDOverrideLibraryProperty *op_old =
+ id_override_old->override_library->properties.first;
+ for (; op_new; op_new = op_new->next, op_old = op_old->next) {
+ lib_override_library_property_copy(op_new, op_old);
+ }
}
}
+ else {
+ /* Add to proper main list, ensure unique name for local ID, sort, and clear relevant
+ * tags. */
+ BKE_libblock_management_main_add(bmain, id_override_new);
+ }
}
}
FOREACH_MAIN_LISTBASE_ID_END;
}
FOREACH_MAIN_LISTBASE_END;
+ /* We need to remap old to new override usages in a separate loop, after all new overrides have
+ * been added to Main. */
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
+ ID *id_override_new = id->newid;
+ ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
+
+ if (id_override_old != NULL) {
+ /* Remap all IDs to use the new override. */
+ BKE_libblock_remap(bmain, id_override_old, id_override_new, 0);
+ /* Remap no-main override IDs we just created too. */
+ GHashIterator linkedref_to_old_override_iter;
+ GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
+ ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter);
+ if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) {
+ BKE_libblock_relink_ex(bmain,
+ id_override_old_iter,
+ id_override_old,
+ id_override_new,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ }
+ }
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ BKE_main_collection_sync(bmain);
+
/* We need to apply override rules in a separate loop, after all ID pointers have been properly
* remapped, and all new local override IDs have gotten their proper original names, otherwise
* override operations based on those ID names would fail. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ continue;
+ }
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
- if (id_override_old != NULL) {
+ if (id_override_old == NULL) {
+ continue;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)) {
/* Apply rules on new override ID using old one as 'source' data. */
/* Note that since we already remapped ID pointers in old override IDs to new ones, we
* can also apply ID pointer override rules safely here. */
@@ -1032,29 +1206,44 @@ bool BKE_lib_override_library_resync(Main *bmain,
RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS :
RNA_OVERRIDE_APPLY_FLAG_NOP);
}
+
+ /* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages
+ * of the old one.
+ * This is necessary in case said old ID is not in Main anymore. */
+ BKE_libblock_relink_ex(bmain,
+ id_override_old,
+ NULL,
+ NULL,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT;
}
}
FOREACH_MAIN_ID_END;
/* Delete old override IDs.
- * Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT.
- * This improves performances anyway, so everything is fine. */
+ * Note that we have to use tagged group deletion here, since ID deletion also uses
+ * LIB_TAG_DOIT. This improves performances anyway, so everything is fine. */
+ int user_edited_overrides_deletion_count = 0;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT) {
- /* Note that this works because linked IDs are always after local ones (including overrides),
- * so we will only ever tag an old override ID after we have already checked it in this loop,
- * hence we cannot untag it later. */
- if (id->newid != NULL && ID_IS_LINKED(id)) {
+ /* Note that this works because linked IDs are always after local ones (including
+ * overrides), so we will only ever tag an old override ID after we have already checked it
+ * in this loop, hence we cannot untag it later. */
+ if (id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
if (id_override_old != NULL) {
id->newid->tag &= ~LIB_TAG_DOIT;
id_override_old->tag |= LIB_TAG_DOIT;
+ if (id_override_old->tag & LIB_TAG_NO_MAIN) {
+ BKE_id_free(bmain, id_override_old);
+ }
}
}
id->tag &= ~LIB_TAG_DOIT;
}
- /* Also deal with old overrides that went missing in new linked data. */
+ /* Also deal with old overrides that went missing in new linked data - only for real local
+ * overrides for now, not those who are linked. */
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) {
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id));
if (!BKE_lib_override_library_is_user_edited(id)) {
@@ -1063,15 +1252,32 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->tag &= ~LIB_TAG_MISSING;
CLOG_INFO(&LOG, 2, "Old override %s is being deleted", id->name);
}
+#if 0
else {
/* Otherwise, keep them, user needs to decide whether what to do with them. */
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
id_fake_user_set(id);
+ id->flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name);
}
+#else
+ else {
+ /* Delete them nevertheless, with fat warning, user needs to decide whether they want to
+ * save that version of the file (and accept the loss), or not. */
+ id->tag |= LIB_TAG_DOIT;
+ id->tag &= ~LIB_TAG_MISSING;
+ CLOG_WARN(
+ &LOG, "Old override %s is being deleted even though it was user-edited", id->name);
+ user_edited_overrides_deletion_count++;
+ }
+#endif
}
}
FOREACH_MAIN_ID_END;
+
+ /* Cleanup, many pointers in this GHash are already invalid now. */
+ BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
+
BKE_id_multi_tagged_delete(bmain);
/* At this point, `id_root` has very likely been deleted, we need to update it to its new
@@ -1079,64 +1285,161 @@ bool BKE_lib_override_library_resync(Main *bmain,
*/
id_root = id_root_reference->newid;
- /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
- /* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
- * since we already relinked old root override collection to new resync'ed one above. So this
- * call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
- * that we do not have any stray objects. */
- lib_override_library_create_post_process(bmain,
- scene,
- view_layer,
- id_root_reference,
- id_root,
- override_resync_residual_storage,
- true);
+ if (user_edited_overrides_deletion_count > 0) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "During resync of data-block %s, %d obsolete overrides were deleted, that had "
+ "local changes defined by user",
+ id_root->name + 2,
+ user_edited_overrides_deletion_count);
+ }
+
+ if (do_post_process) {
+ /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
+ /* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
+ * since we already relinked old root override collection to new resync'ed one above. So this
+ * call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
+ * that we do not have any stray objects. */
+ lib_override_library_create_post_process(bmain,
+ scene,
+ view_layer,
+ id_root_reference,
+ id_root,
+ override_resync_residual_storage,
+ true);
+ }
/* Cleanup. */
- BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
-
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* That one should not be needed in fact. */
- /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
- BKE_lib_override_library_main_operations_create(bmain, true);
-
return success;
}
-/**
- * Detect and handle required resync of overrides data, when relations between reference linked IDs
- * have changed.
+/* Also tag ancestors overrides for resync.
*
- * This is a fairly complex and costly operation, typically it should be called after
- * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
+ * WARNING: Expects `bmain` to have valid relation data.
*
- * This function will first detect the remaining cases requiring a resync (namely, either when an
- * existing linked ID that did not require to be overridden before now would be, or when new IDs
- * are added to the hierarchy).
+ * NOTE: Related to `lib_override_library_main_resync_find_root_recurse` below.
*
- * Then it will handle the resync of necessary IDs (through calls to
- * #BKE_lib_override_library_resync).
+ * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to
+ * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and
+ * resync the whole hierarchy.
*/
-void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer)
+static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
+ ID *id,
+ const int library_indirect_level)
{
- /* We use a specific collection to gather/store all 'orphaned' override collections and objects
- * generated by re-sync-process. This avoids putting them in scene's master collection. */
-#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS"
- Collection *override_resync_residual_storage = BLI_findstring(
- &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2);
- if (override_resync_residual_storage != NULL &&
- override_resync_residual_storage->id.lib != NULL) {
- override_resync_residual_storage = NULL;
+ if (id->lib != NULL && id->lib->temp_index > library_indirect_level) {
+ CLOG_ERROR(
+ &LOG,
+ "While processing indirect level %d, ID %s from lib %s of indirect level %d detected "
+ "as needing resync.",
+ library_indirect_level,
+ id->name,
+ id->lib->filepath,
+ id->lib->temp_index);
}
- if (override_resync_residual_storage == NULL) {
- override_resync_residual_storage = BKE_collection_add(
- bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME);
- /* Hide the collection from viewport and render. */
- override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
+
+ MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
+ BLI_assert(entry != NULL);
+
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) {
+ /* This ID has already been processed. */
+ return;
+ }
+ /* This way we won't process again that ID, should we encounter it again through another
+ * relationship hierarchy. */
+ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED;
+
+ for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != NULL;
+ entry_item = entry_item->next) {
+ if (entry_item->usage_flag &
+ (IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE | IDWALK_CB_LOOPBACK)) {
+ continue;
+ }
+ ID *id_from = entry_item->id_pointer.from;
+
+ /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
+ if (id_from != id && ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib) {
+ id_from->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ CLOG_INFO(&LOG,
+ 4,
+ "ID %s (%p) now tagged as needing resync because they use %s (%p) that needs to "
+ "be overridden",
+ id_from->name,
+ id_from->lib,
+ id->name,
+ id->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id_from, library_indirect_level);
+ }
+ }
+}
+
+/* Ensures parent collection (or objects) in the same override group are also tagged for resync.
+ *
+ * This is needed since otherwise, some (new) ID added in one sub-collection might be used in
+ * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those
+ * sub-collections would be unaware that this is the same ID, and would re-generate several
+ * overrides for it.
+ *
+ * NOTE: Related to `lib_override_resync_tagging_finalize` above.
+ */
+static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level)
+{
+ (*level)++;
+ ID *return_id = id;
+
+ switch (GS(id->name)) {
+ case ID_GR: {
+ /* Find the highest valid collection in the parenting hierarchy.
+ * Note that in practice, in any decent common case there is only one well defined root
+ * collection anyway. */
+ int max_level = *level;
+ Collection *collection = (Collection *)id;
+ LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) {
+ Collection *collection_parent = collection_parent_iter->collection;
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) &&
+ collection_parent->id.lib == id->lib) {
+ int tmp_level = *level;
+ ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id,
+ &tmp_level);
+ if (tmp_level > max_level) {
+ max_level = tmp_level;
+ return_id = tmp_id;
+ }
+ }
+ }
+ break;
+ }
+ case ID_OB: {
+ Object *object = (Object *)id;
+ if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) &&
+ object->parent->id.lib == id->lib) {
+ return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level);
+ }
+ break;
+ }
+ default:
+ break;
}
+ return return_id;
+}
+
+/* Ensure resync of all overrides at one level of indirect usage.
+ *
+ * We need to handle each level independently, since an override at level n may be affected by
+ * other overrides from level n + 1 etc. (i.e. from linked overrides it may use).
+ */
+static void lib_override_library_main_resync_on_library_indirect_level(
+ Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ Collection *override_resync_residual_storage,
+ const int library_indirect_level,
+ ReportList *reports)
+{
BKE_main_relations_create(bmain, 0);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
@@ -1148,7 +1451,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
* those used by current existing overrides. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
@@ -1159,7 +1462,8 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
LibOverrideGroupTagData data = {.bmain = bmain,
.id_root = id->override_library->reference,
.tag = LIB_TAG_DOIT,
- .missing_tag = LIB_TAG_MISSING};
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = false};
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
lib_override_hierarchy_dependencies_recursive_tag(&data);
@@ -1169,13 +1473,15 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
/* Now check existing overrides, those needing resync will be the one either already tagged as
* such, or the one using linked data that is now tagged as needing override. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
- CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name);
+ CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level);
continue;
}
@@ -1184,21 +1490,23 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != NULL;
entry_item = entry_item->next) {
- if (entry_item->usage_flag &
- (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
+ if (entry_item->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) {
continue;
}
ID *id_to = *entry_item->id_pointer.to;
/* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
- if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) {
+ if (ID_IS_LINKED(id_to) && (id_to->lib != id->lib) && (id_to->tag & LIB_TAG_DOIT) != 0) {
id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(&LOG,
3,
- "ID %s now tagged as needing resync because they use linked %s that now needs "
- "to be overridden",
+ "ID %s (%p) now tagged as needing resync because they use linked %s (%p) that "
+ "now needs to be overridden",
id->name,
- id_to->name);
+ id->lib,
+ id_to->name,
+ id_to->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level);
break;
}
}
@@ -1213,21 +1521,31 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
* `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */
bool do_continue = true;
while (do_continue) {
- ListBase *lb;
do_continue = false;
+ ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
- if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
+ if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 ||
+ (ID_IS_LINKED(id) && id->lib->temp_index < library_indirect_level) ||
+ (!ID_IS_LINKED(id) && library_indirect_level != 0)) {
continue;
}
+
+ int level = 0;
+ /* In complex non-supported cases, with several different override hierarchies sharing
+ * relations between each-other, we may end up not actually updating/replacing the given
+ * root id (see e.g. pro/shots/110_rextoria/110_0150_A/110_0150_A.anim.blend of sprites
+ * project repository, r2687).
+ * This can lead to infinite loop here, at least avoid this. */
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ id = lib_override_library_main_resync_find_root_recurse(id, &level);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
- if (ID_IS_LINKED(id)) {
- continue;
- }
do_continue = true;
- CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
+
+ CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, id->lib);
const bool success = BKE_lib_override_library_resync(
- bmain, scene, view_layer, id, override_resync_residual_storage, false);
+ bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
break;
}
@@ -1238,6 +1556,117 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
}
FOREACH_MAIN_LISTBASE_END;
}
+}
+
+static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data)
+{
+ if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) {
+ return IDWALK_RET_NOP;
+ }
+ ID *id_owner = cb_data->id_owner;
+ ID *id = *cb_data->id_pointer;
+ if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) {
+ const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0;
+ if (owner_library_indirect_level > 10000) {
+ CLOG_ERROR(
+ &LOG,
+ "Levels of indirect usages of libraries is way too high, skipping further building "
+ "loops (Involves at least '%s' and '%s')",
+ id_owner->lib->filepath,
+ id->lib->filepath);
+ BLI_assert(0);
+ return IDWALK_RET_NOP;
+ }
+
+ if (owner_library_indirect_level >= id->lib->temp_index) {
+ id->lib->temp_index = owner_library_indirect_level + 1;
+ *(bool *)cb_data->user_data = true;
+ }
+ }
+ return IDWALK_RET_NOP;
+}
+
+/** Define the `temp_index` of libraries from their highest level of indirect usage.
+ *
+ * E.g. if lib_a uses lib_b, lib_c and lib_d, and lib_b also uses lib_d, then lib_a has an index of
+ * 1, lib_b and lib_c an index of 2, and lib_d an index of 3. */
+static int lib_override_libraries_index_define(Main *bmain)
+{
+ LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
+ /* index 0 is reserved for local data. */
+ library->temp_index = 1;
+ }
+ bool do_continue = true;
+ while (do_continue) {
+ do_continue = false;
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ BKE_library_foreach_ID_link(
+ bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY);
+ }
+ FOREACH_MAIN_ID_END;
+ }
+
+ int library_indirect_level_max = 0;
+ LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
+ if (library->temp_index > library_indirect_level_max) {
+ library_indirect_level_max = library->temp_index;
+ }
+ }
+ return library_indirect_level_max;
+}
+
+/**
+ * Detect and handle required resync of overrides data, when relations between reference linked IDs
+ * have changed.
+ *
+ * This is a fairly complex and costly operation, typically it should be called after
+ * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
+ *
+ * This function will first detect the remaining cases requiring a resync (namely, either when an
+ * existing linked ID that did not require to be overridden before now would be, or when new IDs
+ * are added to the hierarchy).
+ *
+ * Then it will handle the resync of necessary IDs (through calls to
+ * #BKE_lib_override_library_resync).
+ */
+void BKE_lib_override_library_main_resync(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ReportList *reports)
+{
+ /* We use a specific collection to gather/store all 'orphaned' override collections and objects
+ * generated by re-sync-process. This avoids putting them in scene's master collection. */
+#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS"
+ Collection *override_resync_residual_storage = BLI_findstring(
+ &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2);
+ if (override_resync_residual_storage != NULL &&
+ override_resync_residual_storage->id.lib != NULL) {
+ override_resync_residual_storage = NULL;
+ }
+ if (override_resync_residual_storage == NULL) {
+ override_resync_residual_storage = BKE_collection_add(
+ bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME);
+ /* Hide the collection from viewport and render. */
+ override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT |
+ COLLECTION_RESTRICT_RENDER;
+ }
+
+ int library_indirect_level = lib_override_libraries_index_define(bmain);
+ while (library_indirect_level >= 0) {
+ /* Update overrides from each indirect level separately. */
+ lib_override_library_main_resync_on_library_indirect_level(bmain,
+ scene,
+ view_layer,
+ override_resync_residual_storage,
+ library_indirect_level,
+ reports);
+ library_indirect_level--;
+ }
+
+ /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
+ lib_override_library_create_post_process(
+ bmain, scene, view_layer, NULL, NULL, override_resync_residual_storage, true);
if (BKE_collection_is_empty(override_resync_residual_storage)) {
BKE_collection_delete(bmain, override_resync_residual_storage, true);
@@ -1258,9 +1687,12 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
/* Tag all library overrides in the chains of dependencies from the given root one. */
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
- lib_override_local_group_tag(&data);
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = true};
+ lib_override_overrides_group_tag(&data);
BKE_main_relations_free(bmain);
@@ -1614,7 +2046,7 @@ bool BKE_lib_override_library_property_operation_operands_validate(
return true;
}
-/** Check against potential \a bmain. */
+/** Check against potential \a bmain. */
void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *reports)
{
if (id->override_library == NULL) {
@@ -1648,7 +2080,7 @@ void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *
}
}
-/** Check against potential \a bmain. */
+/** Check against potential \a bmain. */
void BKE_lib_override_library_main_validate(Main *bmain, ReportList *reports)
{
ID *id;
@@ -1903,10 +2335,11 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
}
struct LibOverrideOpCreateData create_pool_data = {.bmain = bmain, .changed = false};
- TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH);
+ TaskPool *task_pool = BLI_task_pool_create(
+ &create_pool_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
(force_auto || (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) {
/* Usual issue with pose, it's quiet rare but sometimes they may not be up to date when this
* function is called. */
@@ -2049,8 +2482,8 @@ static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain, ID *i
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
to_id_entry = to_id_entry->next) {
- if ((to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) {
- /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as
* actual dependencies. */
continue;
}
@@ -2409,8 +2842,10 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain,
return storage_id;
}
-/** Restore given ID modified by \a BKE_lib_override_library_operations_store_start, to its
- * original state. */
+/**
+ * Restore given ID modified by #BKE_lib_override_library_operations_store_start, to its
+ * original state.
+ */
void BKE_lib_override_library_operations_store_end(
OverrideLibraryStorage *UNUSED(override_storage), ID *local)
{
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index e33743eb36b..b748061ef8a 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -56,11 +56,19 @@ typedef struct LibraryForeachIDData {
*/
ID *self_id;
+ /** Flags controlling the behavior of the 'foreach id' looping code. */
int flag;
+ /** Generic flags to be passed to all callback calls for current processed data. */
int cb_flag;
+ /** Callback flags that are forbidden for all callback calls for current processed data. */
int cb_flag_clear;
+
+ /* Function to call for every ID pointers of current processed data, and its opaque user data
+ * pointer. */
LibraryIDLinkCallback callback;
void *user_data;
+ /** Store the returned value from the callback, to decide how to continue the processing of ID
+ * pointers for current data. */
int status;
/* To handle recursion. */
@@ -73,13 +81,25 @@ bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int
if (!(data->status & IDWALK_STOP)) {
const int flag = data->flag;
ID *old_id = *id_pp;
- const int callback_return = data->callback(&(struct LibraryIDLinkCallbackData){
- .user_data = data->user_data,
- .bmain = data->bmain,
- .id_owner = data->owner_id,
- .id_self = data->self_id,
- .id_pointer = id_pp,
- .cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear)});
+
+ /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic
+ * caller code. */
+ cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear);
+
+ /* Update the callback flags with some extra information regarding overrides: all 'loopback',
+ * 'internal', 'embedded' etc. ID pointers are never overridable. */
+ if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK |
+ IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
+ cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE;
+ }
+
+ const int callback_return = data->callback(
+ &(struct LibraryIDLinkCallbackData){.user_data = data->user_data,
+ .bmain = data->bmain,
+ .id_owner = data->owner_id,
+ .id_self = data->self_id,
+ .id_pointer = id_pp,
+ .cb_flag = cb_flag});
if (flag & IDWALK_READONLY) {
BLI_assert(*(id_pp) == old_id);
}
@@ -132,7 +152,10 @@ void BKE_lib_query_idpropertiesForeachIDLink_callback(IDProperty *id_prop, void
BLI_assert(id_prop->type == IDP_ID);
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
- BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, IDWALK_CB_USER);
+ const int cb_flag = IDWALK_CB_USER | ((id_prop->flag & IDP_FLAG_OVERRIDABLE_LIBRARY) ?
+ 0 :
+ IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
+ BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, cb_flag);
}
bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp)
@@ -221,9 +244,10 @@ static void library_foreach_ID_link(Main *bmain,
* (the node tree), but re-use those generated for the 'owner' ID (the material). */
if (inherit_data == NULL) {
data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0;
- /* When an ID is not in Main database, it should never refcount IDs it is using.
- * Exceptions: NodeTrees (yeah!) directly used by Materials. */
- data.cb_flag_clear = (id->tag & LIB_TAG_NO_MAIN) ? IDWALK_CB_USER | IDWALK_CB_USER_ONE : 0;
+ /* When an ID is defined as not refcounting its ID usages, it should never do it. */
+ data.cb_flag_clear = (id->tag & LIB_TAG_NO_USER_REFCOUNT) ?
+ IDWALK_CB_USER | IDWALK_CB_USER_ONE :
+ 0;
}
else {
data.cb_flag = inherit_data->cb_flag;
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 1f597bbb9a6..2641208897e 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -137,6 +137,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
(id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0);
const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0;
const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0;
+ const bool force_user_refcount = (id_remap_data->flag & ID_REMAP_FORCE_USER_REFCOUNT) != 0;
#ifdef DEBUG_PRINT
printf(
@@ -203,16 +204,16 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
}
}
if (cb_flag & IDWALK_CB_USER) {
- /* NOTE: We don't user-count IDs which are not in the main database.
+ /* NOTE: by default we don't user-count IDs which are not in the main database.
* This is because in certain conditions we can have data-blocks in
* the main which are referencing data-blocks outside of it.
* For example, BKE_mesh_new_from_object() called on an evaluated
* object will cause such situation.
*/
- if ((old_id->tag & LIB_TAG_NO_MAIN) == 0) {
+ if (force_user_refcount || (old_id->tag & LIB_TAG_NO_MAIN) == 0) {
id_us_min(old_id);
}
- if (new_id != NULL && (new_id->tag & LIB_TAG_NO_MAIN) == 0) {
+ if (new_id != NULL && (force_user_refcount || (new_id->tag & LIB_TAG_NO_MAIN) == 0)) {
/* We do not want to handle LIB_TAG_INDIRECT/LIB_TAG_EXTERN here. */
new_id->us++;
}
@@ -303,6 +304,7 @@ static void libblock_remap_data_postprocess_object_update(Main *bmain,
/* Can be called with both old_collection and new_collection being NULL,
* this means we have to check whole Main database then. */
static void libblock_remap_data_postprocess_collection_update(Main *bmain,
+ Collection *owner_collection,
Collection *UNUSED(old_collection),
Collection *new_collection)
{
@@ -311,7 +313,7 @@ static void libblock_remap_data_postprocess_collection_update(Main *bmain,
* and BKE_main_collection_sync_remap() does not tolerate any of those, so for now always check
* whole existing collections for NULL pointers.
* I'd consider optimizing that whole collection remapping process a TODO for later. */
- BKE_collections_child_remove_nulls(bmain, NULL /*old_collection*/);
+ BKE_collections_child_remove_nulls(bmain, owner_collection, NULL /*old_collection*/);
}
else {
/* Temp safe fix, but a "tad" brute force... We should probably be able to use parents from
@@ -523,7 +525,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
break;
case ID_GR:
libblock_remap_data_postprocess_collection_update(
- bmain, (Collection *)old_id, (Collection *)new_id);
+ bmain, NULL, (Collection *)old_id, (Collection *)new_id);
break;
case ID_ME:
case ID_CU:
@@ -628,6 +630,12 @@ void BKE_libblock_relink_ex(
switch (GS(id->name)) {
case ID_SCE:
case ID_GR: {
+ /* Note: here we know which collection we have affected, so at lest for NULL children
+ * detection we can only process that one.
+ * This is also a required fix in case `id` would not be in Main anymore, which can happen
+ * e.g. when called from `id_delete`. */
+ Collection *owner_collection = (GS(id->name) == ID_GR) ? (Collection *)id :
+ ((Scene *)id)->master_collection;
if (old_id) {
switch (GS(old_id->name)) {
case ID_OB:
@@ -636,7 +644,7 @@ void BKE_libblock_relink_ex(
break;
case ID_GR:
libblock_remap_data_postprocess_collection_update(
- bmain, (Collection *)old_id, (Collection *)new_id);
+ bmain, owner_collection, (Collection *)old_id, (Collection *)new_id);
break;
default:
break;
@@ -644,7 +652,7 @@ void BKE_libblock_relink_ex(
}
else {
/* No choice but to check whole objects/collections. */
- libblock_remap_data_postprocess_collection_update(bmain, NULL, NULL);
+ libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL);
libblock_remap_data_postprocess_object_update(bmain, NULL, NULL);
}
break;
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index dc678f248c9..39cc5737ca2 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -538,10 +538,12 @@ ListBase *which_libbase(Main *bmain, short type)
* This is useful for generic traversal of all the blocks in a #Main (by traversing all the lists
* in turn), without worrying about block types.
*
+ * \param lb: Array of lists #INDEX_ID_MAX in length.
+ *
* \note The order of each ID type #ListBase in the array is determined by the `INDEX_ID_<IDTYPE>`
* enum definitions in `DNA_ID.h`. See also the #FOREACH_MAIN_ID_BEGIN macro in `BKE_main.h`
*/
-int set_listbasepointers(Main *bmain, ListBase *lb[INDEX_ID_MAX])
+int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/])
{
/* Libraries may be accessed from pretty much any other ID. */
lb[INDEX_ID_LI] = &(bmain->libraries);
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
index 3a3ad9ef051..a3f122115d8 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -1893,10 +1893,17 @@ static void interp_weights_uv_v2_calc(float r_uv[2],
const float pt_a[2],
const float pt_b[2])
{
+ const float segment_len = len_v2v2(pt_a, pt_b);
+ if (segment_len == 0.0f) {
+ r_uv[0] = 1.0f;
+ r_uv[1] = 0.0f;
+ return;
+ }
+
float pt_on_line[2];
r_uv[0] = closest_to_line_v2(pt_on_line, pt, pt_a, pt_b);
- r_uv[1] = (len_v2v2(pt_on_line, pt) / len_v2v2(pt_a, pt_b)) *
+ r_uv[1] = (len_v2v2(pt_on_line, pt) / segment_len) *
/* This line only sets the sign. */
((line_point_side_v2(pt_a, pt_b, pt) < 0.0f) ? -1.0f : 1.0f);
}
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 37d47a984cc..c5060e16e4d 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -77,6 +77,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "GPU_material.h"
@@ -700,6 +701,114 @@ Material *BKE_object_material_get(Object *ob, short act)
return ma_p ? *ma_p : NULL;
}
+static ID *get_evaluated_object_data_with_materials(Object *ob)
+{
+ ID *data = ob->data;
+ /* Meshes in edit mode need special handling. */
+ if (ob->type == OB_MESH && ob->mode == OB_MODE_EDIT) {
+ Mesh *mesh = ob->data;
+ if (mesh->edit_mesh && mesh->edit_mesh->mesh_eval_final) {
+ data = &mesh->edit_mesh->mesh_eval_final->id;
+ }
+ }
+ return data;
+}
+
+/**
+ * On evaluated objects the number of materials on an object and its data might go out of sync.
+ * This is because during evaluation materials can be added/removed on the object data.
+ *
+ * For rendering or exporting we generally use the materials on the object data. However, some
+ * material indices might be overwritten by the object.
+ */
+Material *BKE_object_material_get_eval(Object *ob, short act)
+{
+ BLI_assert(DEG_is_evaluated_object(ob));
+ const int slot_index = act - 1;
+
+ if (slot_index < 0) {
+ return NULL;
+ }
+ ID *data = get_evaluated_object_data_with_materials(ob);
+ const short *tot_slots_data_ptr = BKE_id_material_len_p(data);
+ const int tot_slots_data = tot_slots_data_ptr ? *tot_slots_data_ptr : 0;
+ if (slot_index >= tot_slots_data) {
+ return NULL;
+ }
+ const int tot_slots_object = ob->totcol;
+
+ Material ***materials_data_ptr = BKE_id_material_array_p(data);
+ Material **materials_data = materials_data_ptr ? *materials_data_ptr : NULL;
+ Material **materials_object = ob->mat;
+
+ /* Check if slot is overwritten by object. */
+ if (slot_index < tot_slots_object) {
+ if (ob->matbits) {
+ if (ob->matbits[slot_index]) {
+ Material *material = materials_object[slot_index];
+ if (material != NULL) {
+ return material;
+ }
+ }
+ }
+ }
+ /* Otherwise use data from object-data. */
+ if (slot_index < tot_slots_data) {
+ Material *material = materials_data[slot_index];
+ return material;
+ }
+ return NULL;
+}
+
+int BKE_object_material_count_eval(Object *ob)
+{
+ BLI_assert(DEG_is_evaluated_object(ob));
+ ID *id = get_evaluated_object_data_with_materials(ob);
+ const short *len_p = BKE_id_material_len_p(id);
+ return len_p ? *len_p : 0;
+}
+
+void BKE_id_material_eval_assign(ID *id, int slot, Material *material)
+{
+ BLI_assert(slot >= 1);
+ Material ***materials_ptr = BKE_id_material_array_p(id);
+ short *len_ptr = BKE_id_material_len_p(id);
+ if (ELEM(NULL, materials_ptr, len_ptr)) {
+ BLI_assert_unreachable();
+ return;
+ }
+
+ const int slot_index = slot - 1;
+ const int old_length = *len_ptr;
+
+ if (slot_index >= old_length) {
+ /* Need to grow slots array. */
+ const int new_length = slot_index + 1;
+ *materials_ptr = MEM_reallocN(*materials_ptr, sizeof(void *) * new_length);
+ *len_ptr = new_length;
+ for (int i = old_length; i < new_length; i++) {
+ (*materials_ptr)[i] = NULL;
+ }
+ }
+
+ (*materials_ptr)[slot_index] = material;
+}
+
+/**
+ * Add an empty material slot if the id has no material slots. This material slot allows the
+ * material to be overwritten by object-linked materials.
+ */
+void BKE_id_material_eval_ensure_default_slot(ID *id)
+{
+ short *len_ptr = BKE_id_material_len_p(id);
+ if (len_ptr == NULL) {
+ return;
+ }
+ if (*len_ptr == 0) {
+ BKE_id_material_eval_assign(id, 1, NULL);
+ }
+}
+
Material *BKE_gpencil_material(Object *ob, short act)
{
Material *ma = BKE_object_material_get(ob, act);
@@ -860,12 +969,6 @@ void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act
act = 1;
}
- /* prevent crashing when using accidentally */
- BLI_assert(!ID_IS_LINKED(ob));
- if (ID_IS_LINKED(ob)) {
- return;
- }
-
/* test arraylens */
totcolp = BKE_object_material_len_p(ob);
@@ -1028,6 +1131,43 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap
BLI_ghash_free(gh_mat_map, NULL, NULL);
}
+/**
+ * Copy materials from evaluated geometry to the original geometry of an object.
+ */
+void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval)
+{
+ ID *data_orig = ob_orig->data;
+
+ short *orig_totcol = BKE_id_material_len_p(data_orig);
+ Material ***orig_mat = BKE_id_material_array_p(data_orig);
+
+ short *eval_totcol = BKE_id_material_len_p(data_eval);
+ Material ***eval_mat = BKE_id_material_array_p(data_eval);
+
+ if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) {
+ return;
+ }
+
+ /* Remove old materials from original geometry. */
+ for (int i = 0; i < *orig_totcol; i++) {
+ id_us_min(&(*orig_mat)[i]->id);
+ }
+ MEM_SAFE_FREE(*orig_mat);
+
+ /* Create new material slots based on materials on evaluated geometry. */
+ *orig_totcol = *eval_totcol;
+ *orig_mat = MEM_callocN(sizeof(void *) * (*eval_totcol), __func__);
+ for (int i = 0; i < *eval_totcol; i++) {
+ Material *material_eval = (*eval_mat)[i];
+ if (material_eval != NULL) {
+ Material *material_orig = (Material *)DEG_get_original_id(&material_eval->id);
+ (*orig_mat)[i] = material_orig;
+ id_us_plus(&material_orig->id);
+ }
+ }
+ BKE_object_materials_test(bmain, ob_orig, data_orig);
+}
+
/* XXX - this calls many more update calls per object then are needed, could be optimized */
void BKE_object_material_array_assign(Main *bmain,
struct Object *ob,
@@ -1367,16 +1507,16 @@ void BKE_texpaint_slots_refresh_object(Scene *scene, struct Object *ob)
}
struct FindTexPaintNodeData {
- bNode *node;
- short iter_index;
- short index;
+ Image *ima;
+ bNode *r_node;
};
static bool texpaint_slot_node_find_cb(bNode *node, void *userdata)
{
struct FindTexPaintNodeData *find_data = userdata;
- if (find_data->iter_index++ == find_data->index) {
- find_data->node = node;
+ Image *ima = (Image *)node->id;
+ if (find_data->ima == ima) {
+ find_data->r_node = node;
return false;
}
@@ -1385,10 +1525,10 @@ static bool texpaint_slot_node_find_cb(bNode *node, void *userdata)
bNode *BKE_texpaint_slot_material_find_node(Material *ma, short texpaint_slot)
{
- struct FindTexPaintNodeData find_data = {NULL, 0, texpaint_slot};
+ struct FindTexPaintNodeData find_data = {ma->texpaintslot[texpaint_slot].ima, NULL};
ntree_foreach_texnode_recursive(ma->nodetree, texpaint_slot_node_find_cb, &find_data);
- return find_data.node;
+ return find_data.r_node;
}
/* r_col = current value, col = new value, (fac == 0) is no change */
diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c
index 1550401cc9c..bb46c7b16c0 100644
--- a/source/blender/blenkernel/intern/mball_tessellate.c
+++ b/source/blender/blenkernel/intern/mball_tessellate.c
@@ -162,7 +162,7 @@ static void make_box_from_metaelem(Box *r, const MetaElem *ml)
}
/**
- * Partitions part of mainb array [start, end) along axis s. Returns i,
+ * Partitions part of #process.mainb array [start, end) along axis s. Returns i,
* where centroids of elements in the [start, i) segment lie "on the right side" of div,
* and elements in the [i, end) segment lie "on the left"
*/
@@ -1170,8 +1170,9 @@ static void polygonize(PROCESS *process)
/**
* Iterates over ALL objects in the scene and all of its sets, including
- * making all duplis(not only metas). Copies metas to mainb array.
- * Computes bounding boxes for building BVH. */
+ * making all duplis (not only meta-elements). Copies meta-elements to #process.mainb array.
+ * Computes bounding boxes for building BVH.
+ */
static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Object *ob)
{
Scene *sce_iter = scene;
@@ -1435,7 +1436,7 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa
if (process.totelem > 0) {
build_bvh_spatial(&process, &process.metaball_bvh, 0, process.totelem, &process.allbb);
- /* Don't polygonize meta-balls with too high resolution (base mball to small)
+ /* Don't polygonize meta-balls with too high resolution (base mball too small)
* note: Eps was 0.0001f but this was giving problems for blood animation for
* the open movie "Sintel", using 0.00001f. */
if (ob->scale[0] > 0.00001f * (process.allbb.max[0] - process.allbb.min[0]) ||
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index ddbc7e7d1ef..0b843c3a97a 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -1521,12 +1521,12 @@ void BKE_mesh_transform(Mesh *me, const float mat[4][4], bool do_keys)
void BKE_mesh_translate(Mesh *me, const float offset[3], const bool do_keys)
{
- MVert *mvert = CustomData_duplicate_referenced_layer(&me->vdata, CD_MVERT, me->totvert);
+ CustomData_duplicate_referenced_layer(&me->vdata, CD_MVERT, me->totvert);
/* If the referenced layer has been re-allocated need to update pointers stored in the mesh. */
BKE_mesh_update_customdata_pointers(me, false);
int i = me->totvert;
- for (mvert = me->mvert; i--; mvert++) {
+ for (MVert *mvert = me->mvert; i--; mvert++) {
add_v3_v3(mvert->co, offset);
}
diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
index 824f791d400..cfb1c192afe 100644
--- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
@@ -28,9 +28,10 @@
#include "BKE_customdata.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
-#include "BKE_mesh_boolean_convert.h"
+#include "BKE_mesh_boolean_convert.hh"
#include "BLI_alloca.h"
+#include "BLI_array.hh"
#include "BLI_float2.hh"
#include "BLI_float4x4.hh"
#include "BLI_math.h"
@@ -51,8 +52,9 @@ constexpr int estimated_max_facelen = 100; /* Used for initial size of some Vect
* so this is a hack to clean up such matrices.
* Would be better to change the transformation code itself.
*/
-static void clean_obmat(float4x4 &cleaned, const float4x4 &mat)
+static float4x4 clean_obmat(const float4x4 &mat)
{
+ float4x4 cleaned;
const float fuzz = 1e-6f;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
@@ -69,6 +71,7 @@ static void clean_obmat(float4x4 &cleaned, const float4x4 &mat)
cleaned.values[i][j] = f;
}
}
+ return cleaned;
}
/* `MeshesToIMeshInfo` keeps track of information used when combining a number
@@ -92,10 +95,10 @@ class MeshesToIMeshInfo {
Array<Face *> mesh_to_imesh_face;
/* Transformation matrix to transform a coordinate in the corresponding
* Mesh to the local space of the first Mesh. */
- Array<float4x4> to_obj0;
+ Array<float4x4> to_target_transform;
/* For each input mesh, how to remap the material slot numbers to
* the material slots in the first mesh. */
- Array<const short *> material_remaps;
+ Span<Array<short>> material_remaps;
/* Total number of input mesh vertices. */
int tot_meshes_verts;
/* Total number of input mesh edges. */
@@ -228,7 +231,8 @@ const MEdge *MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
return medge;
}
-/** Convert all of the meshes in `meshes` to an `IMesh` and return that.
+/**
+ * Convert all of the meshes in `meshes` to an `IMesh` and return that.
* All of the coordinates are transformed into the local space of the
* first Mesh. To do this transformation, we also need the transformation
* obmats corresponding to the Meshes, so they are in the `obmats` argument.
@@ -241,7 +245,8 @@ const MEdge *MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
*/
static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
Span<const float4x4 *> obmats,
- Span<const short *> material_remaps,
+ Span<Array<short>> material_remaps,
+ const float4x4 &target_transform,
IMeshArena &arena,
MeshesToIMeshInfo *r_info)
{
@@ -270,8 +275,8 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
r_info->mesh_vert_offset = Array<int>(nmeshes);
r_info->mesh_edge_offset = Array<int>(nmeshes);
r_info->mesh_poly_offset = Array<int>(nmeshes);
- r_info->to_obj0 = Array<float4x4>(nmeshes);
- r_info->material_remaps = Array<const short *>(nmeshes);
+ r_info->to_target_transform = Array<float4x4>(nmeshes);
+ r_info->material_remaps = material_remaps;
int v = 0;
int e = 0;
int f = 0;
@@ -283,19 +288,10 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
Vector<int, estimated_max_facelen> face_edge_orig;
/* To convert the coordinates of objects 1, 2, etc. into the local space
- * of object 0, we multiply each object's `obmat` by the inverse of
- * object 0's `obmat`. Exact Boolean works better if these matrices
- * are 'cleaned' -- see the comment for the `clean_obmat` function, above. */
- float4x4 obj0_mat;
- float4x4 inv_obj0_mat;
- if (obmats[0] == nullptr) {
- unit_m4(obj0_mat.values);
- unit_m4(inv_obj0_mat.values);
- }
- else {
- clean_obmat(obj0_mat, *obmats[0]);
- invert_m4_m4(inv_obj0_mat.values, obj0_mat.values);
- }
+ * of the target. We multiply each object's `obmat` by the inverse of the
+ * target matrix. Exact Boolean works better if these matrices are 'cleaned'
+ * -- see the comment for the `clean_obmat` function, above. */
+ const float4x4 inv_target_mat = clean_obmat(target_transform).inverted();
/* For each input `Mesh`, make `Vert`s and `Face`s for the corresponding
* `MVert`s and `MPoly`s, and keep track of the original indices (using the
@@ -303,45 +299,34 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
* When making `Face`s, we also put in the original indices for `MEdge`s that
* make up the `MPoly`s using the same scheme. */
for (int mi : meshes.index_range()) {
- float4x4 objn_to_obj0_mat;
const Mesh *me = meshes[mi];
- if (mi == 0) {
- r_info->mesh_vert_offset[mi] = 0;
- r_info->mesh_edge_offset[mi] = 0;
- r_info->mesh_poly_offset[mi] = 0;
- unit_m4(r_info->to_obj0[0].values);
- r_info->material_remaps[0] = nullptr;
- }
- else {
- r_info->mesh_vert_offset[mi] = v;
- r_info->mesh_edge_offset[mi] = e;
- r_info->mesh_poly_offset[mi] = f;
- /* Get matrix that transforms a coordinate in objects[mi]'s local space
- * to object[0]'s local space.*/
- float4x4 objn_mat;
- if (obmats[mi] == nullptr) {
- unit_m4(objn_mat.values);
- }
- else {
- clean_obmat(objn_mat, *obmats[mi]);
- }
- objn_to_obj0_mat = inv_obj0_mat * objn_mat;
- r_info->to_obj0[mi] = objn_to_obj0_mat;
- if (mi < material_remaps.size()) {
- r_info->material_remaps[mi] = material_remaps[mi];
- }
- else {
- r_info->material_remaps[mi] = nullptr;
+ r_info->mesh_vert_offset[mi] = v;
+ r_info->mesh_edge_offset[mi] = e;
+ r_info->mesh_poly_offset[mi] = f;
+ /* Get matrix that transforms a coordinate in objects[mi]'s local space
+ * to the target space space.*/
+ const float4x4 objn_mat = (obmats[mi] == nullptr) ? float4x4::identity() :
+ clean_obmat(*obmats[mi]);
+ r_info->to_target_transform[mi] = inv_target_mat * objn_mat;
+
+ /* Skip the matrix multiplication for each point when there is no transform for a mesh,
+ * for example when the first mesh is already in the target space. (Note the logic directly
+ * above, which uses an identity matrix with a null input transform). */
+ if (obmats[mi] == nullptr) {
+ for (const MVert &vert : Span(me->mvert, me->totvert)) {
+ const float3 co = float3(vert.co);
+ r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(mpq3(co.x, co.y, co.z), v);
+ ++v;
}
}
- for (int vi = 0; vi < me->totvert; ++vi) {
- float3 co = me->mvert[vi].co;
- if (mi > 0) {
- co = objn_to_obj0_mat * co;
+ else {
+ for (const MVert &vert : Span(me->mvert, me->totvert)) {
+ const float3 co = r_info->to_target_transform[mi] * float3(vert.co);
+ r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(mpq3(co.x, co.y, co.z), v);
+ ++v;
}
- r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(mpq3(co.x, co.y, co.z), v);
- ++v;
}
+
for (const MPoly &poly : Span(me->mpoly, me->totpoly)) {
int flen = poly.totloop;
face_vert.clear();
@@ -403,14 +388,14 @@ static void copy_poly_attributes(Mesh *dest_mesh,
const Mesh *orig_me,
int mp_index,
int index_in_orig_me,
- const short *material_remap)
+ Span<short> material_remap)
{
mp->mat_nr = orig_mp->mat_nr;
if (mp->mat_nr >= dest_mesh->totcol) {
mp->mat_nr = 0;
}
else {
- if (material_remap) {
+ if (material_remap.size() > 0) {
short mat_nr = material_remap[orig_mp->mat_nr];
if (mat_nr >= 0 && mat_nr < dest_mesh->totcol) {
mp->mat_nr = mat_nr;
@@ -596,7 +581,7 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
cos_2d = (float(*)[2])BLI_array_alloca(cos_2d, orig_mp->totloop);
weights = Array<float>(orig_mp->totloop);
src_blocks_ofs = Array<const void *>(orig_mp->totloop);
- get_poly2d_cos(orig_me, orig_mp, cos_2d, mim.to_obj0[orig_me_index], axis_mat);
+ get_poly2d_cos(orig_me, orig_mp, cos_2d, mim.to_target_transform[orig_me_index], axis_mat);
}
CustomData *target_cd = &dest_mesh->ldata;
for (int i = 0; i < mp->totloop; ++i) {
@@ -654,7 +639,8 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
}
}
-/** Make sure that there are custom data layers in the target mesh
+/**
+ * Make sure that there are custom data layers in the target mesh
* corresponding to all target layers in all of the operands after the first.
* (The target should already have layers for those in the first operand mesh).
* Edges done separately -- will have to be done later, after edges are made.
@@ -689,7 +675,8 @@ static void merge_edge_customdata_layers(Mesh *target, MeshesToIMeshInfo &mim)
}
}
-/** Convert the output IMesh im to a Blender Mesh,
+/**
+ * Convert the output IMesh im to a Blender Mesh,
* using the information in mim to get all the attributes right.
*/
static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
@@ -743,8 +730,15 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
++cur_loop_index;
}
- copy_poly_attributes(
- result, mp, orig_mp, orig_me, fi, index_in_orig_me, mim.material_remaps[orig_me_index]);
+ copy_poly_attributes(result,
+ mp,
+ orig_mp,
+ orig_me,
+ fi,
+ index_in_orig_me,
+ (mim.material_remaps.size() > 0) ?
+ mim.material_remaps[orig_me_index].as_span() :
+ Span<short>());
copy_or_interp_loop_attributes(result, f, mp, orig_mp, orig_me, orig_me_index, mim);
}
@@ -778,29 +772,40 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
return result;
}
+#endif // WITH_GMP
+
/**
- * Do Exact Boolean directly, without a round trip through #BMesh.
- * The Mesh operands are in `meshes`, with corresponding transforms in in `obmats`.
+ * Do a mesh boolean operation directly on meshes (without going back and forth to BMesh).
+ * \param meshes: An array of of Mesh pointers.
+ * \param obmats: An array of pointers to the obmat matrices that transform local
+ * coordinates to global ones. It is allowed for the pointers to be null, meaning the
+ * transformation is the identity.
+ * \param material_remaps: An array of pointers to arrays of maps from material slot numbers in the
+ * corresponding mesh to the material slot in the first mesh. It is OK for material_remaps or any
+ * of its constituent arrays to be empty.
*/
-static Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
- Span<const float4x4 *> obmats,
- Span<const short *> material_remaps,
- const bool use_self,
- const bool hole_tolerant,
- const BoolOpType boolean_mode)
+Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
+ Span<const float4x4 *> obmats,
+ const float4x4 &target_transform,
+ Span<Array<short>> material_remaps,
+ const bool use_self,
+ const bool hole_tolerant,
+ const int boolean_mode)
{
- const int dbg_level = 0;
+#ifdef WITH_GMP
BLI_assert(meshes.size() == obmats.size());
- const int meshes_len = meshes.size();
- if (meshes_len <= 0) {
+ BLI_assert(material_remaps.size() == 0 || material_remaps.size() == meshes.size());
+ if (meshes.size() <= 0) {
return nullptr;
}
+
+ const int dbg_level = 0;
if (dbg_level > 0) {
- std::cout << "\nDIRECT_MESH_INTERSECT, nmeshes = " << meshes_len << "\n";
+ std::cout << "\nDIRECT_MESH_INTERSECT, nmeshes = " << meshes.size() << "\n";
}
MeshesToIMeshInfo mim;
IMeshArena arena;
- IMesh m_in = meshes_to_imesh(meshes, obmats, material_remaps, arena, &mim);
+ IMesh m_in = meshes_to_imesh(meshes, obmats, material_remaps, target_transform, arena, &mim);
std::function<int(int)> shape_fn = [&mim](int f) {
for (int mi = 0; mi < mim.mesh_poly_offset.size() - 1; ++mi) {
if (f < mim.mesh_poly_offset[mi + 1]) {
@@ -809,61 +814,25 @@ static Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
}
return static_cast<int>(mim.mesh_poly_offset.size()) - 1;
};
- IMesh m_out = boolean_mesh(
- m_in, boolean_mode, meshes_len, shape_fn, use_self, hole_tolerant, nullptr, &arena);
- if (dbg_level > 1) {
+ IMesh m_out = boolean_mesh(m_in,
+ static_cast<BoolOpType>(boolean_mode),
+ meshes.size(),
+ shape_fn,
+ use_self,
+ hole_tolerant,
+ nullptr,
+ &arena);
+ if (dbg_level > 0) {
std::cout << m_out;
write_obj_mesh(m_out, "m_out");
}
return imesh_to_mesh(&m_out, mim);
-}
-
+#else // WITH_GMP
+ UNUSED_VARS(
+ meshes, obmats, material_remaps, target_transform, use_self, hole_tolerant, boolean_mode);
+ return nullptr;
#endif // WITH_GMP
-} // namespace blender::meshintersect
-
-extern "C" {
-
-#ifdef WITH_GMP
-/* Do a mesh boolean directly on meshes (without going back and forth to BMesh).
- * The \a meshes argument is an array of \a meshes_len of Mesh pointers.
- * The \a obmats argument is an array of \a meshes_len of pointers to the obmat
- * The \a material_remaps is an array of pointers to arrays of maps from material
- * slot numbers in the corresponding mesh to the material slot in the first mesh.
- * It is OK for material_remaps or any of its constituent arrays to be NULL.
- * matrices that transform local coordinates to global ones. It is allowed
- * for the pointers to be nullptr, meaning the transformation is the identity. */
-Mesh *BKE_mesh_boolean(const Mesh **meshes,
- const float (*obmats[])[4][4],
- const short **material_remaps,
- const int meshes_len,
- const bool use_self,
- const bool hole_tolerant,
- const int boolean_mode)
-{
- const blender::float4x4 **transforms = (const blender::float4x4 **)obmats;
- return blender::meshintersect::direct_mesh_boolean(
- blender::Span(meshes, meshes_len),
- blender::Span(transforms, meshes_len),
- blender::Span(material_remaps, material_remaps ? meshes_len : 0),
- use_self,
- hole_tolerant,
- static_cast<blender::meshintersect::BoolOpType>(boolean_mode));
-}
-
-#else
-Mesh *BKE_mesh_boolean(const Mesh **UNUSED(meshes),
- const float (*obmats[])[4][4],
- const short **UNUSED(material_remaps),
- const int UNUSED(meshes_len),
- const bool UNUSED(use_self),
- const bool UNUSED(hole_tolerant),
- const int UNUSED(boolean_mode))
-{
- UNUSED_VARS(obmats);
- return NULL;
}
-#endif
-
-} // extern "C"
+} // namespace blender::meshintersect
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 385fd473e63..c698d95ed8d 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1027,11 +1027,15 @@ static Object *object_for_curve_to_mesh_create(Object *object)
*
* Note that there are extra fields in there like bevel and path, but those are not needed during
* conversion, so they are not copied to save unnecessary allocations. */
- if (object->runtime.curve_cache != NULL) {
+ if (temp_object->runtime.curve_cache == NULL) {
temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
"CurveCache for curve types");
+ }
+
+ if (object->runtime.curve_cache != NULL) {
BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
}
+
/* Constructive modifiers will use mesh to store result. */
if (object->runtime.data_eval != NULL) {
BKE_id_copy_ex(
@@ -1057,16 +1061,25 @@ static Object *object_for_curve_to_mesh_create(Object *object)
return temp_object;
}
+/**
+ * Populate `object->runtime.curve_cache` which is then used to create the mesh.
+ */
static void curve_to_mesh_eval_ensure(Object *object)
{
- if (object->runtime.curve_cache == NULL) {
- object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
- }
Curve *curve = (Curve *)object->data;
Curve remapped_curve = *curve;
Object remapped_object = *object;
+ BKE_object_runtime_reset(&remapped_object);
+
remapped_object.data = &remapped_curve;
+ if (object->runtime.curve_cache == NULL) {
+ object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+ }
+
+ /* Temporarily share the curve-cache with the temporary object, owned by `object`. */
+ remapped_object.runtime.curve_cache = object->runtime.curve_cache;
+
/* Clear all modifiers for the bevel object.
*
* This is because they can not be reliably evaluated for an original object (at least because
@@ -1078,6 +1091,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
if (remapped_curve.bevobj != NULL) {
bevel_object = *remapped_curve.bevobj;
BLI_listbase_clear(&bevel_object.modifiers);
+ BKE_object_runtime_reset(&bevel_object);
remapped_curve.bevobj = &bevel_object;
}
@@ -1086,6 +1100,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
if (remapped_curve.taperobj != NULL) {
taper_object = *remapped_curve.taperobj;
BLI_listbase_clear(&taper_object.modifiers);
+ BKE_object_runtime_reset(&taper_object);
remapped_curve.taperobj = &taper_object;
}
@@ -1098,7 +1113,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
* Brecht says hold off with that. */
Mesh *mesh_eval = NULL;
BKE_displist_make_curveTypes_forRender(
- NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval, false);
+ NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, false, &mesh_eval);
/* Note: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a
* real issue currently, code here is broken in more than one way, fix(es) will be done
@@ -1107,8 +1122,12 @@ static void curve_to_mesh_eval_ensure(Object *object)
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
}
- BKE_object_free_curve_cache(&bevel_object);
- BKE_object_free_curve_cache(&taper_object);
+ /* Owned by `object` & needed by the caller to create the mesh. */
+ remapped_object.runtime.curve_cache = NULL;
+
+ BKE_object_runtime_free_data(&remapped_object);
+ BKE_object_runtime_free_data(&taper_object);
+ BKE_object_runtime_free_data(&taper_object);
}
static Mesh *mesh_new_from_curve_type_object(Object *object)
@@ -1192,7 +1211,9 @@ static Mesh *mesh_new_from_mesh(Object *object, Mesh *mesh)
return mesh_result;
}
-static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, Object *object)
+static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph,
+ Object *object,
+ const bool preserve_origindex)
{
if (DEG_is_original_id(&object->id)) {
return mesh_new_from_mesh(object, (Mesh *)object->data);
@@ -1209,16 +1230,23 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, Object
Scene *scene = DEG_get_evaluated_scene(depsgraph);
CustomData_MeshMasks mask = CD_MASK_MESH;
+ if (preserve_origindex) {
+ mask.vmask |= CD_MASK_ORIGINDEX;
+ mask.emask |= CD_MASK_ORIGINDEX;
+ mask.lmask |= CD_MASK_ORIGINDEX;
+ mask.pmask |= CD_MASK_ORIGINDEX;
+ }
Mesh *result = mesh_create_eval_final(depsgraph, scene, &object_for_eval, &mask);
return result;
}
static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
Object *object,
- bool preserve_all_data_layers)
+ const bool preserve_all_data_layers,
+ const bool preserve_origindex)
{
- if (preserve_all_data_layers) {
- return mesh_new_from_mesh_object_with_layers(depsgraph, object);
+ if (preserve_all_data_layers || preserve_origindex) {
+ return mesh_new_from_mesh_object_with_layers(depsgraph, object, preserve_origindex);
}
Mesh *mesh_input = object->data;
/* If we are in edit mode, use evaluated mesh from edit structure, matching to what
@@ -1229,7 +1257,10 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
return mesh_new_from_mesh(object, mesh_input);
}
-Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Object *object, bool preserve_all_data_layers)
+Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
+ Object *object,
+ const bool preserve_all_data_layers,
+ const bool preserve_origindex)
{
Mesh *new_mesh = NULL;
switch (object->type) {
@@ -1242,7 +1273,8 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Object *object, bool preser
new_mesh = mesh_new_from_mball_object(object);
break;
case OB_MESH:
- new_mesh = mesh_new_from_mesh_object(depsgraph, object, preserve_all_data_layers);
+ new_mesh = mesh_new_from_mesh_object(
+ depsgraph, object, preserve_all_data_layers, preserve_origindex);
break;
default:
/* Object does not have geometry data. */
@@ -1307,7 +1339,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
{
BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH));
- Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers);
+ Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
if (mesh == NULL) {
/* Unable to convert the object to a mesh, return an empty one. */
Mesh *mesh_in_bmain = BKE_mesh_add(bmain, ((ID *)object->data)->name + 2);
@@ -1535,7 +1567,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
* check whether it is still true with Mesh */
Mesh tmp = *mesh_dst;
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
- int did_shapekeys = 0;
+ bool did_shapekeys = false;
eCDAllocType alloctype = CD_DUPLICATE;
if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
@@ -1590,7 +1622,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
}
shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
- did_shapekeys = 1;
+ did_shapekeys = true;
}
/* copy texture space */
@@ -1619,13 +1651,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
totedge);
}
if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
- /* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the
- * BKE_mesh_update_customdata_pointers() call below. */
- tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop);
- tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly);
-
- CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop);
- CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly);
+ CustomData_add_layer(&tmp.ldata,
+ CD_MLOOP,
+ CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mloop :
+ MEM_dupallocN(mesh_src->mloop),
+ tmp.totloop);
+ CustomData_add_layer(&tmp.pdata,
+ CD_MPOLY,
+ CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mpoly :
+ MEM_dupallocN(mesh_src->mpoly),
+ tmp.totpoly);
}
/* object had got displacement layer, should copy this layer to save sculpted data */
@@ -1634,15 +1671,16 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
if (totloop == mesh_dst->totloop) {
MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop);
+ if (alloctype == CD_ASSIGN) {
+ /* Assign NULL to prevent double-free. */
+ CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, NULL);
+ }
}
}
/* yes, must be before _and_ after tessellate */
BKE_mesh_update_customdata_pointers(&tmp, false);
- /* since 2.65 caller must do! */
- // BKE_mesh_tessface_calc(&tmp);
-
CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
CustomData_free(&mesh_dst->fdata, mesh_dst->totface);
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index eee1b763be5..826094420a7 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -1715,7 +1715,8 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
loop_split_generator(NULL, &common_data);
}
else {
- TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH);
+ TaskPool *task_pool = BLI_task_pool_create(
+ &common_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
loop_split_generator(task_pool, &common_data);
@@ -2351,7 +2352,7 @@ float BKE_mesh_calc_poly_uv_area(const MPoly *mpoly, const MLoopUV *uv_array)
}
/* finally calculate the area */
- area = area_poly_v2((const float(*)[2])vertexcos, (unsigned int)mpoly->totloop);
+ area = area_poly_v2(vertexcos, (uint)mpoly->totloop);
return area;
}
@@ -2566,7 +2567,7 @@ bool BKE_mesh_center_median_from_polys(const Mesh *me, float r_cent[3])
const MLoop *mloop = me->mloop;
const MVert *mvert = me->mvert;
zero_v3(r_cent);
- for (mpoly = me->mpoly; i--; mpoly++) {
+ for (; i--; mpoly++) {
int loopend = mpoly->loopstart + mpoly->totloop;
for (int j = mpoly->loopstart; j < loopend; j++) {
add_v3_v3(r_cent, mvert[mloop[j].v].co);
diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc
index ac6dd96ed90..2a364b183b2 100644
--- a/source/blender/blenkernel/intern/mesh_fair.cc
+++ b/source/blender/blenkernel/intern/mesh_fair.cc
@@ -172,7 +172,7 @@ class FairingContext {
}
/* Early return, nothing to do. */
- if (num_affected_vertices == 0 || num_affected_vertices == totvert_) {
+ if (ELEM(num_affected_vertices, 0, totvert_)) {
return;
}
diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c
index a22b52d68d5..3d30c218fba 100644
--- a/source/blender/blenkernel/intern/mesh_mirror.c
+++ b/source/blender/blenkernel/intern/mesh_mirror.c
@@ -51,7 +51,7 @@ Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mm
(axis == 1 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Y) ||
(axis == 2 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Z));
- const float bisect_distance = 0.001f;
+ const float bisect_distance = mmd->bisect_threshold;
Mesh *result;
BMesh *bm;
@@ -183,6 +183,19 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
if (do_bisect) {
copy_v3_v3(plane_co, itmp[3]);
copy_v3_v3(plane_no, itmp[axis]);
+
+ /* Account for non-uniform scale in `ob`, see: T87592. */
+ float ob_scale[3] = {
+ len_squared_v3(ob->obmat[0]),
+ len_squared_v3(ob->obmat[1]),
+ len_squared_v3(ob->obmat[2]),
+ };
+ /* Scale to avoid precision loss with extreme values. */
+ const float ob_scale_max = max_fff(UNPACK3(ob_scale));
+ if (LIKELY(ob_scale_max != 0.0f)) {
+ mul_v3_fl(ob_scale, 1.0f / ob_scale_max);
+ mul_v3_v3(plane_no, ob_scale);
+ }
}
}
else if (do_bisect) {
diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c
index f3b29171762..58d2a24f15b 100644
--- a/source/blender/blenkernel/intern/mesh_remap.c
+++ b/source/blender/blenkernel/intern/mesh_remap.c
@@ -2399,8 +2399,7 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
}
tot_rays *= tot_rays;
- poly_area_2d_inv = area_poly_v2((const float(*)[2])poly_vcos_2d,
- (unsigned int)mp->totloop);
+ poly_area_2d_inv = area_poly_v2(poly_vcos_2d, (uint)mp->totloop);
/* In case we have a null-area degenerated poly... */
poly_area_2d_inv = 1.0f / max_ff(poly_area_2d_inv, 1e-9f);
diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc
new file mode 100644
index 00000000000..91c9951ae89
--- /dev/null
+++ b/source/blender/blenkernel/intern/mesh_sample.cc
@@ -0,0 +1,158 @@
+/*
+ * 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_attribute_math.hh"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+namespace blender::bke::mesh_surface_sample {
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+template<typename T>
+BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ const Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ const T v0 = data_in[v0_index];
+ const T v1 = data_in[v1_index];
+ const T v2 = data_in[v2_index];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+void sample_point_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_out.size() == bary_coords.size());
+ BLI_assert(data_in.size() == mesh.totvert);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_point_attribute<T>(
+ mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+template<typename T>
+BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int loop_index_0 = looptri.tri[0];
+ const int loop_index_1 = looptri.tri[1];
+ const int loop_index_2 = looptri.tri[2];
+
+ const T v0 = data_in[loop_index_0];
+ const T v1 = data_in[loop_index_1];
+ const T v2 = data_in[loop_index_2];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+void sample_corner_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_out.size() == bary_coords.size());
+ BLI_assert(data_in.size() == mesh.totloop);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_corner_attribute<T>(
+ mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+template<typename T>
+void sample_face_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : data_out.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const int poly_index = looptri.poly;
+ data_out[i] = data_in[poly_index];
+ }
+}
+
+void sample_face_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_in.size() == mesh.totpoly);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c
index 2e22e521a13..6a7ff0851f5 100644
--- a/source/blender/blenkernel/intern/mesh_tangent.c
+++ b/source/blender/blenkernel/intern/mesh_tangent.c
@@ -656,7 +656,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
/* Calculation */
if (looptri_len != 0) {
- TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
+ TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
tangent_mask_curr = 0;
/* Calculate tangent layers */
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 34b7c4234ec..e60f0102b9a 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -281,7 +281,7 @@ bool BKE_modifier_is_preview(ModifierData *md)
return false;
}
-ModifierData *BKE_modifiers_findby_type(Object *ob, ModifierType type)
+ModifierData *BKE_modifiers_findby_type(const Object *ob, ModifierType type)
{
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == type) {
@@ -291,7 +291,7 @@ ModifierData *BKE_modifiers_findby_type(Object *ob, ModifierType type)
return NULL;
}
-ModifierData *BKE_modifiers_findby_name(Object *ob, const char *name)
+ModifierData *BKE_modifiers_findby_name(const Object *ob, const char *name)
{
return BLI_findstring(&(ob->modifiers), name, offsetof(ModifierData, name));
}
@@ -729,7 +729,6 @@ Object *BKE_modifiers_is_deformed_by_armature(Object *ob)
ArmatureGpencilModifierData *agmd = NULL;
GpencilModifierData *gmd = BKE_gpencil_modifiers_get_virtual_modifierlist(
ob, &gpencilvirtualModifierData);
- gmd = ob->greasepencil_modifiers.first;
/* return the first selected armature, this lets us use multiple armatures */
for (; gmd; gmd = gmd->next) {
@@ -749,7 +748,6 @@ Object *BKE_modifiers_is_deformed_by_armature(Object *ob)
VirtualModifierData virtualModifierData;
ArmatureModifierData *amd = NULL;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- md = ob->modifiers.first;
/* return the first selected armature, this lets us use multiple armatures */
for (; md; md = md->next) {
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 9c2cd03dbc2..0f2f56f4f2b 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -950,7 +950,7 @@ static void movieclip_load_get_size(MovieClip *clip)
int width, height;
MovieClipUser user = {0};
- user.framenr = 1;
+ user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, 1);
BKE_movieclip_get_size(clip, &user, &width, &height);
if (width && height) {
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index 63b14c30b3c..92e21183acb 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -254,7 +254,7 @@ NlaTrack *BKE_nlatrack_copy(Main *bmain,
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
* flags in BKE_lib_id.h
*/
-void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag)
+void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag)
{
NlaTrack *nlt, *nlt_d;
@@ -275,6 +275,54 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int fl
}
}
+/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */
+static void update_active_strip(AnimData *adt_dest,
+ NlaTrack *track_dest,
+ const AnimData *adt_source,
+ NlaTrack *track_source)
+{
+ BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips));
+
+ NlaStrip *strip_dest = track_dest->strips.first;
+ LISTBASE_FOREACH (NlaStrip *, strip_source, &track_source->strips) {
+ if (strip_source == adt_source->actstrip) {
+ adt_dest->actstrip = strip_dest;
+ }
+
+ strip_dest = strip_dest->next;
+ }
+}
+
+/* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */
+static void update_active_track(AnimData *adt_dest, const AnimData *adt_source)
+{
+ BLI_assert(BLI_listbase_count(&adt_source->nla_tracks) ==
+ BLI_listbase_count(&adt_dest->nla_tracks));
+
+ NlaTrack *track_dest = adt_dest->nla_tracks.first;
+ LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) {
+ if (track_source == adt_source->act_track) {
+ adt_dest->act_track = track_dest;
+ /* Assumption: the active strip is on the active track. */
+ update_active_strip(adt_dest, track_dest, adt_source, track_source);
+ }
+
+ track_dest = track_dest->next;
+ }
+}
+
+void BKE_nla_tracks_copy_from_adt(Main *bmain,
+ AnimData *adt_dest,
+ const AnimData *adt_source,
+ const int flag)
+{
+ adt_dest->act_track = NULL;
+ adt_dest->actstrip = NULL;
+
+ BKE_nla_tracks_copy(bmain, &adt_dest->nla_tracks, &adt_source->nla_tracks, flag);
+ update_active_track(adt_dest, adt_source);
+}
+
/* Adding ------------------------------------------- */
/* Add a NLA Track to the given AnimData
@@ -434,8 +482,10 @@ NlaStrip *BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker)
return strip;
}
-/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
- * `IDTypeInfo` structure). */
+/**
+ * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
+ * `IDTypeInfo` structure).
+ */
void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data)
{
BKE_LIB_FOREACHID_PROCESS(data, strip->act, IDWALK_CB_USER);
@@ -1380,8 +1430,10 @@ static void nlastrip_fix_resize_overlaps(NlaStrip *strip)
}
}
-/** Recalculate the start and end frames for the strip to match the bounds of its action such that
- * the overall NLA animation result is unchanged. */
+/**
+ * Recalculate the start and end frames for the strip to match the bounds of its action such that
+ * the overall NLA animation result is unchanged.
+ */
void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip)
{
float prev_actstart;
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index d06e4030117..1c82218fc65 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -303,6 +303,16 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -434,6 +444,12 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case SOCK_COLLECTION:
BLO_write_struct(writer, bNodeSocketValueCollection, sock->default_value);
break;
+ case SOCK_TEXTURE:
+ BLO_write_struct(writer, bNodeSocketValueTexture, sock->default_value);
+ break;
+ case SOCK_MATERIAL:
+ BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value);
+ break;
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
@@ -497,10 +513,16 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
if (node->storage) {
/* could be handlerized at some point, now only 1 exception still */
- if ((ntree->type == NTREE_SHADER) &&
+ if ((ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY)) &&
ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) {
BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage);
}
+ else if ((ntree->type == NTREE_GEOMETRY) && (node->type == GEO_NODE_ATTRIBUTE_CURVE_MAP)) {
+ BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_vec);
+ BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_rgb);
+ }
else if (ntree->type == NTREE_SHADER && (node->type == SH_NODE_SCRIPT)) {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
if (nss->bytecode) {
@@ -676,6 +698,18 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
BKE_curvemapping_blend_read(reader, (CurveMapping *)node->storage);
break;
}
+ case GEO_NODE_ATTRIBUTE_CURVE_MAP: {
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BLO_read_data_address(reader, &data->curve_vec);
+ if (data->curve_vec) {
+ BKE_curvemapping_blend_read(reader, data->curve_vec);
+ }
+ BLO_read_data_address(reader, &data->curve_rgb);
+ if (data->curve_rgb) {
+ BKE_curvemapping_blend_read(reader, data->curve_rgb);
+ }
+ break;
+ }
case SH_NODE_SCRIPT: {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
BLO_read_data_address(reader, &nss->bytecode);
@@ -778,6 +812,13 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
{
IDP_BlendReadLib(reader, sock->prop);
+ /* This can happen for all socket types when a file is saved in an older version of Blender than
+ * it was originally created in (T86298). Some socket types still require a default value. The
+ * default value of those sockets will be created in `ntreeSetTypes`. */
+ if (sock->default_value == nullptr) {
+ return;
+ }
+
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value;
@@ -795,6 +836,16 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
BLO_read_id_address(reader, lib, &default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -880,6 +931,16 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
BLO_expand(expander, default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1307,8 +1368,8 @@ void nodeUnregisterType(bNodeType *nt)
bool nodeTypeUndefined(bNode *node)
{
return (node->typeinfo == &NodeTypeUndefined) ||
- (node->type == NODE_GROUP && node->id && ID_IS_LINKED(node->id) &&
- (node->id->tag & LIB_TAG_MISSING));
+ ((node->type == NODE_GROUP || node->type == NODE_CUSTOM_GROUP) && node->id &&
+ ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING));
}
GHashIterator *nodeTypeGetIterator(void)
@@ -1364,7 +1425,9 @@ GHashIterator *nodeSocketTypeGetIterator(void)
return BLI_ghashIterator_new(nodesockettypes_hash);
}
-struct bNodeSocket *nodeFindSocket(const bNode *node, int in_out, const char *identifier)
+struct bNodeSocket *nodeFindSocket(const bNode *node,
+ eNodeSocketInOut in_out,
+ const char *identifier)
{
const ListBase *sockets = (in_out == SOCK_IN) ? &node->inputs : &node->outputs;
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
@@ -1445,6 +1508,16 @@ static void socket_id_user_increment(bNodeSocket *sock)
id_us_plus((ID *)default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1484,6 +1557,20 @@ static void socket_id_user_decrement(bNodeSocket *sock)
}
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ if (default_value->value != nullptr) {
+ id_us_min(&default_value->value->id);
+ }
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ if (default_value->value != nullptr) {
+ id_us_min(&default_value->value->id);
+ }
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1521,7 +1608,7 @@ void nodeModifySocketType(
bNodeSocket *nodeAddSocket(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *identifier,
const char *name)
@@ -1543,7 +1630,7 @@ bNodeSocket *nodeAddSocket(bNodeTree *ntree,
bNodeSocket *nodeInsertSocket(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
bNodeSocket *next_sock,
const char *identifier,
@@ -1575,6 +1662,8 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketFloatAngle";
case PROP_TIME:
return "NodeSocketFloatTime";
+ case PROP_TIME_ABSOLUTE:
+ return "NodeSocketFloatTimeAbsolute";
case PROP_DISTANCE:
return "NodeSocketFloatDistance";
case PROP_NONE:
@@ -1627,6 +1716,10 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketGeometry";
case SOCK_COLLECTION:
return "NodeSocketCollection";
+ case SOCK_TEXTURE:
+ return "NodeSocketTexture";
+ case SOCK_MATERIAL:
+ return "NodeSocketMaterial";
}
return nullptr;
}
@@ -1646,6 +1739,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceFloatAngle";
case PROP_TIME:
return "NodeSocketInterfaceFloatTime";
+ case PROP_TIME_ABSOLUTE:
+ return "NodeSocketInterfaceFloatTimeAbsolute";
case PROP_DISTANCE:
return "NodeSocketInterfaceFloatDistance";
case PROP_NONE:
@@ -1698,13 +1793,17 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceGeometry";
case SOCK_COLLECTION:
return "NodeSocketInterfaceCollection";
+ case SOCK_TEXTURE:
+ return "NodeSocketInterfaceTexture";
+ case SOCK_MATERIAL:
+ return "NodeSocketInterfaceMaterial";
}
return nullptr;
}
bNodeSocket *nodeAddStaticSocket(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
int type,
int subtype,
const char *identifier,
@@ -1724,7 +1823,7 @@ bNodeSocket *nodeAddStaticSocket(bNodeTree *ntree,
bNodeSocket *nodeInsertStaticSocket(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
int type,
int subtype,
bNodeSocket *next_sock,
@@ -1998,7 +2097,8 @@ bNode *nodeAddStaticNode(const struct bContext *C, bNodeTree *ntree, int type)
/* do an extra poll here, because some int types are used
* for multiple node types, this helps find the desired type
*/
- if (ntype->type == type && (!ntype->poll || ntype->poll(ntype, ntree))) {
+ const char *disabled_hint;
+ if (ntype->type == type && (!ntype->poll || ntype->poll(ntype, ntree, &disabled_hint))) {
idname = ntype->idname;
break;
}
@@ -2162,6 +2262,17 @@ bNodeTree *ntreeCopyTree_ex_new_pointers(const bNodeTree *ntree,
return new_ntree;
}
+static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
+{
+ int count = 0;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (ELEM(socket, link->fromsock, link->tosock)) {
+ count++;
+ }
+ }
+ return count;
+}
+
/* also used via rna api, so we check for proper input output direction */
bNodeLink *nodeAddLink(
bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
@@ -2198,6 +2309,10 @@ bNodeLink *nodeAddLink(
ntree->update |= NTREE_UPDATE_LINKS;
}
+ if (link->tosock->flag & SOCK_MULTI_INPUT) {
+ link->multi_input_socket_index = node_count_links(ntree, link->tosock) - 1;
+ }
+
return link;
}
@@ -3087,10 +3202,12 @@ void ntreeSetOutput(bNodeTree *ntree)
* might be different for editor or for "real" use... */
}
-/** Get address of potential nodetree pointer of given ID.
+/**
+ * Get address of potential node-tree pointer of given ID.
*
* \warning Using this function directly is potentially dangerous, if you don't know or are not
- * sure, please use `ntreeFromID()` instead. */
+ * sure, please use `ntreeFromID()` instead.
+ */
bNodeTree **BKE_ntree_ptr_from_id(ID *id)
{
switch (GS(id->name)) {
@@ -3220,13 +3337,11 @@ void ntreeLocalMerge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
/* ************ NODE TREE INTERFACE *************** */
static bNodeSocket *make_socket_interface(bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *name)
{
bNodeSocketType *stype = nodeSocketTypeFind(idname);
- int own_index = ntree->cur_index++;
-
if (stype == nullptr) {
return nullptr;
}
@@ -3238,7 +3353,7 @@ static bNodeSocket *make_socket_interface(bNodeTree *ntree,
sock->type = SOCK_CUSTOM; /* int type undefined by default */
/* assign new unique index */
- own_index = ntree->cur_index++;
+ const int own_index = ntree->cur_index++;
/* use the own_index as socket identifier */
if (in_out == SOCK_IN) {
BLI_snprintf(sock->identifier, MAX_NAME, "Input_%d", own_index);
@@ -3256,7 +3371,9 @@ static bNodeSocket *make_socket_interface(bNodeTree *ntree,
return sock;
}
-bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree, int in_out, const char *identifier)
+bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree,
+ eNodeSocketInOut in_out,
+ const char *identifier)
{
ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs;
LISTBASE_FOREACH (bNodeSocket *, iosock, sockets) {
@@ -3268,7 +3385,7 @@ bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree, int in_out, const char *
}
bNodeSocket *ntreeAddSocketInterface(bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *name)
{
@@ -3284,8 +3401,11 @@ bNodeSocket *ntreeAddSocketInterface(bNodeTree *ntree,
return iosock;
}
-bNodeSocket *ntreeInsertSocketInterface(
- bNodeTree *ntree, int in_out, const char *idname, bNodeSocket *next_sock, const char *name)
+bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree,
+ eNodeSocketInOut in_out,
+ const char *idname,
+ bNodeSocket *next_sock,
+ const char *name)
{
bNodeSocket *iosock = make_socket_interface(ntree, in_out, idname, name);
if (in_out == SOCK_IN) {
@@ -3304,7 +3424,7 @@ struct bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree,
bNodeSocket *from_sock)
{
bNodeSocket *iosock = ntreeAddSocketInterface(
- ntree, from_sock->in_out, from_sock->idname, from_sock->name);
+ ntree, static_cast<eNodeSocketInOut>(from_sock->in_out), from_sock->idname, from_sock->name);
if (iosock) {
if (iosock->typeinfo->interface_from_socket) {
iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock);
@@ -3319,7 +3439,11 @@ struct bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree,
bNodeSocket *from_sock)
{
bNodeSocket *iosock = ntreeInsertSocketInterface(
- ntree, from_sock->in_out, from_sock->idname, next_sock, from_sock->name);
+ ntree,
+ static_cast<eNodeSocketInOut>(from_sock->in_out),
+ from_sock->idname,
+ next_sock,
+ from_sock->name);
if (iosock) {
if (iosock->typeinfo->interface_from_socket) {
iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock);
@@ -4205,7 +4329,7 @@ void ntreeUpdateAllUsers(Main *main, ID *id)
if (GS(id->name) == ID_NT) {
bNodeTree *ngroup = (bNodeTree *)id;
- if (ngroup->type == NTREE_GEOMETRY) {
+ if (ngroup->type == NTREE_GEOMETRY && (ngroup->update & NTREE_UPDATE_GROUP)) {
LISTBASE_FOREACH (Object *, object, &main->objects) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
@@ -4387,15 +4511,17 @@ static void node_type_base_defaults(bNodeType *ntype)
}
/* allow this node for any tree type */
-static bool node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *UNUSED(ntree))
+static bool node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *UNUSED(ntree),
+ const char **UNUSED(disabled_hint))
{
return true;
}
/* use the basic poll function */
-static bool node_poll_instance_default(bNode *node, bNodeTree *ntree)
+static bool node_poll_instance_default(bNode *node, bNodeTree *ntree, const char **disabled_hint)
{
- return node->typeinfo->poll(node->typeinfo, ntree);
+ return node->typeinfo->poll(node->typeinfo, ntree, disabled_hint);
}
/* NOLINTNEXTLINE: readability-function-size */
@@ -4614,7 +4740,9 @@ void node_type_internal_links(bNodeType *ntype,
/* callbacks for undefined types */
-static bool node_undefined_poll(bNodeType *UNUSED(ntype), bNodeTree *UNUSED(nodetree))
+static bool node_undefined_poll(bNodeType *UNUSED(ntype),
+ bNodeTree *UNUSED(nodetree),
+ const char **UNUSED(r_disabled_hint))
{
/* this type can not be added deliberately, it's just a placeholder */
return false;
@@ -4693,6 +4821,7 @@ static void registerCompositNodes()
register_node_type_cmp_defocus();
register_node_type_cmp_sunbeams();
register_node_type_cmp_denoise();
+ register_node_type_cmp_antialiasing();
register_node_type_cmp_valtorgb();
register_node_type_cmp_rgbtobw();
@@ -4903,31 +5032,46 @@ static void registerGeometryNodes()
register_node_type_geo_group();
register_node_type_geo_align_rotation_to_vector();
+ register_node_type_geo_attribute_clamp();
register_node_type_geo_attribute_color_ramp();
register_node_type_geo_attribute_combine_xyz();
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_convert();
+ register_node_type_geo_attribute_curve_map();
register_node_type_geo_attribute_fill();
+ register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_mix();
register_node_type_geo_attribute_proximity();
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_separate_xyz();
+ register_node_type_geo_attribute_transfer();
register_node_type_geo_attribute_vector_math();
+ register_node_type_geo_attribute_vector_rotate();
register_node_type_geo_attribute_remove();
register_node_type_geo_boolean();
+ register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
+ register_node_type_geo_convex_hull();
+ register_node_type_geo_curve_length();
+ register_node_type_geo_curve_to_mesh();
+ register_node_type_geo_curve_resample();
+ register_node_type_geo_delete_geometry();
register_node_type_geo_edge_split();
+ register_node_type_geo_input_material();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
+ register_node_type_geo_material_assign();
+ register_node_type_geo_material_replace();
register_node_type_geo_mesh_primitive_circle();
register_node_type_geo_mesh_primitive_cone();
register_node_type_geo_mesh_primitive_cube();
register_node_type_geo_mesh_primitive_cylinder();
+ register_node_type_geo_mesh_primitive_grid();
register_node_type_geo_mesh_primitive_ico_sphere();
register_node_type_geo_mesh_primitive_line();
- register_node_type_geo_mesh_primitive_plane();
register_node_type_geo_mesh_primitive_uv_sphere();
+ register_node_type_geo_mesh_to_curve();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
@@ -4937,8 +5081,10 @@ static void registerGeometryNodes()
register_node_type_geo_point_translate();
register_node_type_geo_points_to_volume();
register_node_type_geo_sample_texture();
+ register_node_type_geo_select_by_material();
register_node_type_geo_subdivide();
register_node_type_geo_subdivision_surface();
+ register_node_type_geo_switch();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_volume_to_mesh();
diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc
index f2a152ac00d..e5e9f00c7c3 100644
--- a/source/blender/blenkernel/intern/node_ui_storage.cc
+++ b/source/blender/blenkernel/intern/node_ui_storage.cc
@@ -39,7 +39,7 @@ using blender::Vector;
* bNodeTree struct in DNA. This could change if the node tree had a runtime struct. */
static std::mutex global_ui_storage_mutex;
-static void ui_storage_ensure(bNodeTree &ntree)
+static NodeTreeUIStorage &ui_storage_ensure(bNodeTree &ntree)
{
/* As an optimization, only acquire a lock if the UI storage doesn't exist,
* because it only needs to be allocated once for every node tree. */
@@ -50,6 +50,7 @@ static void ui_storage_ensure(bNodeTree &ntree)
ntree.ui_storage = new NodeTreeUIStorage();
}
}
+ return *ntree.ui_storage;
}
const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C,
@@ -90,7 +91,7 @@ void BKE_nodetree_ui_storage_free_for_context(bNodeTree &ntree,
{
NodeTreeUIStorage *ui_storage = ntree.ui_storage;
if (ui_storage != nullptr) {
- std::lock_guard<std::mutex> lock(ui_storage->context_map_mutex);
+ std::lock_guard<std::mutex> lock(ui_storage->mutex);
ui_storage->context_map.remove(context);
}
}
@@ -126,20 +127,14 @@ static void node_error_message_log(bNodeTree &ntree,
}
}
-static NodeUIStorage &node_ui_storage_ensure(bNodeTree &ntree,
+static NodeUIStorage &node_ui_storage_ensure(NodeTreeUIStorage &locked_ui_storage,
const NodeTreeEvaluationContext &context,
const bNode &node)
{
- ui_storage_ensure(ntree);
- NodeTreeUIStorage &ui_storage = *ntree.ui_storage;
-
- std::lock_guard<std::mutex> lock(ui_storage.context_map_mutex);
Map<std::string, NodeUIStorage> &node_tree_ui_storage =
- ui_storage.context_map.lookup_or_add_default(context);
-
+ locked_ui_storage.context_map.lookup_or_add_default(context);
NodeUIStorage &node_ui_storage = node_tree_ui_storage.lookup_or_add_default_as(
StringRef(node.name));
-
return node_ui_storage;
}
@@ -149,9 +144,12 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
const NodeWarningType type,
std::string message)
{
+ NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree);
+ std::lock_guard lock{ui_storage.mutex};
+
node_error_message_log(ntree, node, message, type);
- NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
+ NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node);
node_ui_storage.warnings.append({type, std::move(message)});
}
@@ -162,7 +160,10 @@ void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const AttributeDomain domain,
const CustomDataType data_type)
{
- NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
- node_ui_storage.attribute_hints.add_as(attribute_name,
- AvailableAttributeInfo{domain, data_type});
+ NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree);
+ std::lock_guard lock{ui_storage.mutex};
+
+ NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node);
+ node_ui_storage.attribute_hints.add_as(
+ AvailableAttributeInfo{attribute_name, domain, data_type});
}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index b07c4b22c39..b73f6a5b78c 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -311,8 +311,8 @@ static void object_free_data(ID *id)
/* Free runtime curves data. */
if (ob->runtime.curve_cache) {
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
- if (ob->runtime.curve_cache->path) {
- free_path(ob->runtime.curve_cache->path);
+ if (ob->runtime.curve_cache->anim_path_accum_length) {
+ MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
MEM_freeN(ob->runtime.curve_cache);
ob->runtime.curve_cache = NULL;
@@ -1188,8 +1188,8 @@ void BKE_object_free_curve_cache(Object *ob)
if (ob->runtime.curve_cache) {
BKE_displist_free(&ob->runtime.curve_cache->disp);
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
- if (ob->runtime.curve_cache->path) {
- free_path(ob->runtime.curve_cache->path);
+ if (ob->runtime.curve_cache->anim_path_accum_length) {
+ MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
BKE_nurbList_free(&ob->runtime.curve_cache->deformed_nurbs);
MEM_freeN(ob->runtime.curve_cache);
@@ -1332,12 +1332,9 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
if (ob->type == OB_HAIR) {
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
- if (ob->type == OB_POINTCLOUD) {
+ if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) {
return (mti->modifyGeometrySet != NULL);
}
- if (ob->type == OB_VOLUME) {
- return (mti->modifyVolume != NULL);
- }
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {
return false;
@@ -1359,8 +1356,9 @@ static bool object_modifier_type_copy_check(ModifierType md_type)
return !ELEM(md_type, eModifierType_Hook, eModifierType_Collision);
}
-/** Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings
- * ID), or add one, and return valid `psys` from `ob_dst`.
+/**
+ * Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings ID),
+ * or add one, and return valid `psys` from `ob_dst`.
*
* \note Order handling is fairly weak here. This code assumes that it is called **before** the
* modifier using the psys is actually copied, and that this copied modifier will be added at the
@@ -1392,7 +1390,8 @@ static ParticleSystem *object_copy_modifier_particle_system_ensure(Main *bmain,
return psys_dst;
}
-/** Copy a single modifier.
+/**
+ * Copy a single modifier.
*
* \note **Do not** use this function to copy a whole modifier stack (see note below too). Use
* `BKE_object_modifier_stack_copy` instead.
@@ -1400,7 +1399,8 @@ static ParticleSystem *object_copy_modifier_particle_system_ensure(Main *bmain,
* \note Complex modifiers relaying on other data (like e.g. dynamic paint or fluid using particle
* systems) are not always 100% 'correctly' copied here, since we have to use heuristics to decide
* which particle system to use or add in `ob_dst`, and it's placement in the stack, etc. If used
- * more than once, this function should preferably be called in stack order. */
+ * more than once, this function should preferably be called in stack order.
+ */
bool BKE_object_copy_modifier(
Main *bmain, Scene *scene, Object *ob_dst, const Object *ob_src, ModifierData *md_src)
{
@@ -1500,10 +1500,12 @@ bool BKE_object_copy_modifier(
return true;
}
-/** Copy a single GPencil modifier.
+/**
+ * Copy a single GPencil modifier.
*
* \note **Do not** use this function to copy a whole modifier stack. Use
- * `BKE_object_modifier_stack_copy` instead. */
+ * `BKE_object_modifier_stack_copy` instead.
+ */
bool BKE_object_copy_gpencil_modifier(struct Object *ob_dst, GpencilModifierData *gmd_src)
{
BLI_assert(ob_dst->type == OB_GPENCIL);
@@ -1756,6 +1758,10 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_geometry_set_free(ob->runtime.geometry_set_eval);
ob->runtime.geometry_set_eval = NULL;
}
+ if (ob->runtime.geometry_set_previews != NULL) {
+ BLI_ghash_free(ob->runtime.geometry_set_previews, NULL, (GHashValFreeFP)BKE_geometry_set_free);
+ ob->runtime.geometry_set_previews = NULL;
+ }
}
void BKE_object_free_caches(Object *object)
@@ -1806,6 +1812,24 @@ void BKE_object_free_caches(Object *object)
}
}
+/* Can be called from multiple threads. */
+void BKE_object_preview_geometry_set_add(Object *ob,
+ const uint64_t key,
+ struct GeometrySet *geometry_set)
+{
+ static ThreadMutex mutex = BLI_MUTEX_INITIALIZER;
+ BLI_mutex_lock(&mutex);
+ if (ob->runtime.geometry_set_previews == NULL) {
+ ob->runtime.geometry_set_previews = BLI_ghash_int_new(__func__);
+ }
+ BLI_ghash_reinsert(ob->runtime.geometry_set_previews,
+ POINTER_FROM_UINT(key),
+ geometry_set,
+ NULL,
+ (GHashValFreeFP)BKE_geometry_set_free);
+ BLI_mutex_unlock(&mutex);
+}
+
/**
* Actual check for internal data, not context or flags.
*/
@@ -2265,7 +2289,7 @@ Object *BKE_object_add_for_data(
void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int flag)
{
SoftBody *sb = ob_src->soft;
- bool tagged_no_main = ob_dst->id.tag & LIB_TAG_NO_MAIN;
+ const bool is_orig = (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0;
ob_dst->softflag = ob_src->softflag;
if (sb == NULL) {
@@ -2306,7 +2330,7 @@ void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int fl
sbn->scratch = NULL;
- if (!tagged_no_main) {
+ if (is_orig) {
sbn->shared = MEM_dupallocN(sb->shared);
sbn->shared->pointcache = BKE_ptcache_copy_list(
&sbn->shared->ptcaches, &sb->shared->ptcaches, flag);
@@ -2345,7 +2369,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f
BLI_listbase_clear(&psysn->pathcachebufs);
BLI_listbase_clear(&psysn->childcachebufs);
- if (flag & LIB_ID_CREATE_NO_MAIN) {
+ if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
/* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview
* creation. */
// BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0);
@@ -2598,8 +2622,8 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src)
*
* \note This function does not do any remapping to new IDs, caller must do it
* (\a #BKE_libblock_relink_to_newid()).
- * \note Caller MUST free \a newid pointers itself (#BKE_main_id_clear_newpoins()) and call updates
- * of DEG too (#DAG_relations_tag_update()).
+ * \note Caller MUST free \a newid pointers itself (#BKE_main_id_newptr_and_tag_clear()) and call
+ * updates of DEG too (#DAG_relations_tag_update()).
*/
Object *BKE_object_duplicate(Main *bmain,
Object *ob,
@@ -2609,8 +2633,7 @@ Object *BKE_object_duplicate(Main *bmain,
const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
if (!is_subprocess) {
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
/* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate
* all expected linked data. */
if (ID_IS_LINKED(ob)) {
@@ -2749,8 +2772,7 @@ Object *BKE_object_duplicate(Main *bmain,
#endif
/* Cleanup. */
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
}
if (obn->type == OB_ARMATURE) {
@@ -3244,7 +3266,7 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
if (par->runtime.curve_cache == NULL) {
return false;
}
- if (par->runtime.curve_cache->path == NULL) {
+ if (par->runtime.curve_cache->anim_path_accum_length == NULL) {
return false;
}
@@ -3260,12 +3282,16 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
else {
ctime = cu->ctime;
}
- CLAMP(ctime, 0.0f, 1.0f);
+
+ if (cu->flag & CU_PATH_CLAMP) {
+ CLAMP(ctime, 0.0f, 1.0f);
+ }
unit_m4(r_mat);
/* vec: 4 items! */
- if (where_on_path(par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : NULL, &radius, NULL)) {
+ if (BKE_where_on_path(
+ par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : NULL, &radius, NULL)) {
if (cu->flag & CU_FOLLOW) {
quat_apply_track(quat, ob->trackflag, ob->upflag);
normalize_qt(quat);
@@ -4108,7 +4134,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
const bool use_hidden)
{
bool ok = false;
- if ((ob->transflag & OB_DUPLI) == 0) {
+ if ((ob->transflag & OB_DUPLI) == 0 && ob->runtime.geometry_set_eval == NULL) {
return ok;
}
@@ -4319,7 +4345,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
}
/* Speed optimization for animation lookups. */
if (ob->pose != NULL) {
- BKE_pose_channels_hash_make(ob->pose);
+ BKE_pose_channels_hash_ensure(ob->pose);
if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(ob->pose);
}
@@ -4353,8 +4379,6 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
BKE_object_handle_data_update(depsgraph, scene, ob);
}
- ob->id.recalc &= ID_RECALC_ALL;
-
object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update);
}
@@ -4474,6 +4498,37 @@ Mesh *BKE_object_get_original_mesh(Object *object)
return result;
}
+Lattice *BKE_object_get_lattice(const Object *object)
+{
+ ID *data = object->data;
+ if (data == NULL || GS(data->name) != ID_LT) {
+ return NULL;
+ }
+
+ Lattice *lt = (Lattice *)data;
+ if (lt->editlatt) {
+ return lt->editlatt->latt;
+ }
+
+ return lt;
+}
+
+Lattice *BKE_object_get_evaluated_lattice(const Object *object)
+{
+ ID *data_eval = object->runtime.data_eval;
+
+ if (data_eval == NULL || GS(data_eval->name) != ID_LT) {
+ return NULL;
+ }
+
+ Lattice *lt_eval = (Lattice *)data_eval;
+ if (lt_eval->editlatt) {
+ return lt_eval->editlatt->latt;
+ }
+
+ return lt_eval;
+}
+
static int pc_cmp(const void *a, const void *b)
{
const LinkData *ad = a, *bd = b;
@@ -5061,6 +5116,20 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
}
/**
+ * The function frees memory used by the runtime data, but not the runtime field itself.
+ *
+ * All runtime data is cleared to ensure it's not used again,
+ * in keeping with other `_free_data(..)` functions.
+ */
+void BKE_object_runtime_free_data(Object *object)
+{
+ /* Currently this is all that's needed. */
+ BKE_object_free_derived_caches(object);
+
+ BKE_object_runtime_reset(object);
+}
+
+/**
* Find an associated armature object.
*/
static Object *obrel_armature_find(Object *ob)
@@ -5601,7 +5670,7 @@ Mesh *BKE_object_to_mesh(Depsgraph *depsgraph, Object *object, bool preserve_all
{
BKE_object_to_mesh_clear(object);
- Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers);
+ Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
object->runtime.object_as_temp_mesh = mesh;
return mesh;
}
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.cc
index 632e7519f05..768fa9373c1 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -21,18 +21,22 @@
* \ingroup bke
*/
-#include <limits.h>
-#include <stddef.h>
-#include <stdlib.h>
+#include <climits>
+#include <cstddef>
+#include <cstdlib>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_string_utf8.h"
-#include "BLI_alloca.h"
+#include "BLI_array.hh"
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
#include "BLI_math.h"
#include "BLI_rand.h"
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
#include "DNA_anim_types.h"
#include "DNA_collection_types.h"
@@ -48,6 +52,7 @@
#include "BKE_editmesh_cache.h"
#include "BKE_font.h"
#include "BKE_geometry_set.h"
+#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lattice.h"
@@ -65,11 +70,17 @@
#include "BLI_hash.h"
#include "BLI_strict_flags.h"
+using blender::Array;
+using blender::float3;
+using blender::float4x4;
+using blender::Span;
+using blender::Vector;
+
/* -------------------------------------------------------------------- */
/** \name Internal Duplicate Context
* \{ */
-typedef struct DupliContext {
+struct DupliContext {
Depsgraph *depsgraph;
/** XXX child objects are selected from this group if set, could be nicer. */
Collection *collection;
@@ -81,6 +92,13 @@ typedef struct DupliContext {
Object *object;
float space_mat[4][4];
+ /**
+ * A stack that contains all the "parent" objects of a particular instance when recursive
+ * instancing is used. This is used to prevent objects from instancing themselves accidentally.
+ * Use a vector instead of a stack because we want to use the #contains method.
+ */
+ Vector<Object *> *instance_stack;
+
int persistent_id[MAX_DUPLI_RECUR];
int level;
@@ -88,12 +106,12 @@ typedef struct DupliContext {
/** Result containers. */
ListBase *duplilist; /* Legacy doubly-linked list. */
-} DupliContext;
+};
-typedef struct DupliGenerator {
+struct DupliGenerator {
short type; /* Dupli Type, see members of #OB_DUPLI. */
void (*make_duplis)(const DupliContext *ctx);
-} DupliGenerator;
+};
static const DupliGenerator *get_dupli_generator(const DupliContext *ctx);
@@ -104,15 +122,17 @@ static void init_context(DupliContext *r_ctx,
Depsgraph *depsgraph,
Scene *scene,
Object *ob,
- const float space_mat[4][4])
+ const float space_mat[4][4],
+ Vector<Object *> &instance_stack)
{
r_ctx->depsgraph = depsgraph;
r_ctx->scene = scene;
r_ctx->view_layer = DEG_get_evaluated_view_layer(depsgraph);
- r_ctx->collection = NULL;
+ r_ctx->collection = nullptr;
r_ctx->object = ob;
r_ctx->obedit = OBEDIT_FROM_OBACT(ob);
+ r_ctx->instance_stack = &instance_stack;
if (space_mat) {
copy_m4_m4(r_ctx->space_mat, space_mat);
}
@@ -123,7 +143,7 @@ static void init_context(DupliContext *r_ctx,
r_ctx->gen = get_dupli_generator(r_ctx);
- r_ctx->duplilist = NULL;
+ r_ctx->duplilist = nullptr;
}
/**
@@ -141,6 +161,7 @@ static void copy_dupli_context(
}
r_ctx->object = ob;
+ r_ctx->instance_stack = ctx->instance_stack;
if (mat) {
mul_m4_m4m4(r_ctx->space_mat, (float(*)[4])ctx->space_mat, mat);
}
@@ -165,11 +186,11 @@ static DupliObject *make_dupli(const DupliContext *ctx,
/* Add a #DupliObject instance to the result container. */
if (ctx->duplilist) {
- dob = MEM_callocN(sizeof(DupliObject), "dupli object");
+ dob = (DupliObject *)MEM_callocN(sizeof(DupliObject), "dupli object");
BLI_addtail(ctx->duplilist, dob);
}
else {
- return NULL;
+ return nullptr;
}
dob->ob = ob;
@@ -226,12 +247,19 @@ static void make_recursive_duplis(const DupliContext *ctx,
const float space_mat[4][4],
int index)
{
+ if (ctx->instance_stack->contains(ob)) {
+ /* Avoid recursive instances. */
+ printf("Warning: '%s' object is trying to instance itself.\n", ob->id.name + 2);
+ return;
+ }
/* Simple preventing of too deep nested collections with #MAX_DUPLI_RECUR. */
if (ctx->level < MAX_DUPLI_RECUR) {
DupliContext rctx;
copy_dupli_context(&rctx, ctx, ob, space_mat, index);
if (rctx.gen) {
+ ctx->instance_stack->append(ob);
rctx.gen->make_duplis(&rctx);
+ ctx->instance_stack->remove_last();
}
}
}
@@ -242,7 +270,7 @@ static void make_recursive_duplis(const DupliContext *ctx,
/** \name Internal Child Duplicates (Used by Other Functions)
* \{ */
-typedef void (*MakeChildDuplisFunc)(const DupliContext *ctx, void *userdata, Object *child);
+using MakeChildDuplisFunc = void (*)(const DupliContext *ctx, void *userdata, Object *child);
static bool is_child(const Object *ob, const Object *parent)
{
@@ -270,7 +298,7 @@ static void make_child_duplis(const DupliContext *ctx,
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (ctx->collection, ob, mode) {
if ((ob != ctx->obedit) && is_child(ob, parent)) {
DupliContext pctx;
- copy_dupli_context(&pctx, ctx, ctx->object, NULL, _base_id);
+ copy_dupli_context(&pctx, ctx, ctx->object, nullptr, _base_id);
/* Meta-balls have a different dupli handling. */
if (ob->type != OB_MBALL) {
@@ -282,13 +310,13 @@ static void make_child_duplis(const DupliContext *ctx,
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
}
else {
- int baseid = 0;
+ int baseid;
ViewLayer *view_layer = ctx->view_layer;
- for (Base *base = view_layer->object_bases.first; base; base = base->next, baseid++) {
+ LISTBASE_FOREACH_INDEX (Base *, base, &view_layer->object_bases, baseid) {
Object *ob = base->object;
if ((ob != ctx->obedit) && is_child(ob, parent)) {
DupliContext pctx;
- copy_dupli_context(&pctx, ctx, ctx->object, NULL, baseid);
+ copy_dupli_context(&pctx, ctx, ctx->object, nullptr, baseid);
/* Meta-balls have a different dupli-handling. */
if (ob->type != OB_MBALL) {
@@ -316,30 +344,30 @@ static Mesh *mesh_data_from_duplicator_object(Object *ob,
BMEditMesh *em = BKE_editmesh_from_object(ob);
Mesh *me_eval;
- *r_em = NULL;
- *r_vert_coords = NULL;
- if (r_vert_normals != NULL) {
- *r_vert_normals = NULL;
+ *r_em = nullptr;
+ *r_vert_coords = nullptr;
+ if (r_vert_normals != nullptr) {
+ *r_vert_normals = nullptr;
}
/* We do not need any render-specific handling anymore, depsgraph takes care of that. */
/* NOTE: Do direct access to the evaluated mesh: this function is used
* during meta balls evaluation. But even without those all the objects
* which are needed for correct instancing are already evaluated. */
- if (em != NULL) {
+ if (em != nullptr) {
/* Note that this will only show deformation if #eModifierMode_OnCage is enabled.
* We could change this but it matches 2.7x behavior. */
me_eval = em->mesh_eval_cage;
- if ((me_eval == NULL) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
- EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : NULL;
+ if ((me_eval == nullptr) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
+ EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : nullptr;
/* Only assign edit-mesh in the case we can't use `me_eval`. */
*r_em = em;
- me_eval = NULL;
+ me_eval = nullptr;
- if ((emd != NULL) && (emd->vertexCos != NULL)) {
+ if ((emd != nullptr) && (emd->vertexCos != nullptr)) {
*r_vert_coords = emd->vertexCos;
- if (r_vert_normals != NULL) {
+ if (r_vert_normals != nullptr) {
BKE_editmesh_cache_ensure_vert_normals(em, emd);
*r_vert_normals = emd->vertexNos;
}
@@ -364,7 +392,7 @@ static void make_duplis_collection(const DupliContext *ctx)
Collection *collection;
float collection_mat[4][4];
- if (ob->instance_collection == NULL) {
+ if (ob->instance_collection == nullptr) {
return;
}
collection = ob->instance_collection;
@@ -404,7 +432,7 @@ static const DupliGenerator gen_dupli_collection = {
* \{ */
/** Values shared between different mesh types. */
-typedef struct VertexDupliData_Params {
+struct VertexDupliData_Params {
/**
* It's important we use this context instead of the `ctx` passed into #make_child_duplis
* since these won't match in the case of recursion.
@@ -412,23 +440,23 @@ typedef struct VertexDupliData_Params {
const DupliContext *ctx;
bool use_rotation;
-} VertexDupliData_Params;
+};
-typedef struct VertexDupliData_Mesh {
+struct VertexDupliData_Mesh {
VertexDupliData_Params params;
int totvert;
const MVert *mvert;
const float (*orco)[3];
-} VertexDupliData_Mesh;
+};
-typedef struct VertexDupliData_EditMesh {
+struct VertexDupliData_EditMesh {
VertexDupliData_Params params;
BMEditMesh *em;
- /* Can be NULL. */
+ /* Can be nullptr. */
const float (*vert_coords)[3];
const float (*vert_normals)[3];
@@ -440,7 +468,7 @@ typedef struct VertexDupliData_EditMesh {
* edit-mesh to be converted into a mesh.
*/
bool has_orco;
-} VertexDupliData_EditMesh;
+};
/**
* \param no: The direction,
@@ -505,7 +533,7 @@ static void make_child_duplis_verts_from_mesh(const DupliContext *ctx,
void *userdata,
Object *inst_ob)
{
- VertexDupliData_Mesh *vdd = userdata;
+ VertexDupliData_Mesh *vdd = (VertexDupliData_Mesh *)userdata;
const bool use_rotation = vdd->params.use_rotation;
const MVert *mvert = vdd->mvert;
@@ -519,7 +547,8 @@ static void make_child_duplis_verts_from_mesh(const DupliContext *ctx,
const MVert *mv = mvert;
for (int i = 0; i < totvert; i++, mv++) {
const float *co = mv->co;
- const float no[3] = {UNPACK3(mv->no)};
+ float no[3];
+ normal_short_to_float_v3(no, mv->no);
DupliObject *dob = vertex_dupli(vdd->params.ctx, inst_ob, child_imat, i, co, no, use_rotation);
if (vdd->orco) {
copy_v3_v3(dob->orco, vdd->orco[i]);
@@ -531,7 +560,7 @@ static void make_child_duplis_verts_from_editmesh(const DupliContext *ctx,
void *userdata,
Object *inst_ob)
{
- VertexDupliData_EditMesh *vdd = userdata;
+ VertexDupliData_EditMesh *vdd = (VertexDupliData_EditMesh *)userdata;
BMEditMesh *em = vdd->em;
const bool use_rotation = vdd->params.use_rotation;
@@ -549,9 +578,9 @@ static void make_child_duplis_verts_from_editmesh(const DupliContext *ctx,
BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) {
const float *co, *no;
- if (vert_coords != NULL) {
+ if (vert_coords != nullptr) {
co = vert_coords[i];
- no = vert_normals ? vert_normals[i] : NULL;
+ no = vert_normals ? vert_normals[i] : nullptr;
}
else {
co = v->co;
@@ -571,37 +600,34 @@ static void make_duplis_verts(const DupliContext *ctx)
const bool use_rotation = parent->transflag & OB_DUPLIROT;
/* Gather mesh info. */
- BMEditMesh *em = NULL;
- const float(*vert_coords)[3] = NULL;
- const float(*vert_normals)[3] = NULL;
+ BMEditMesh *em = nullptr;
+ const float(*vert_coords)[3] = nullptr;
+ const float(*vert_normals)[3] = nullptr;
Mesh *me_eval = mesh_data_from_duplicator_object(
- parent, &em, &vert_coords, use_rotation ? &vert_normals : NULL);
- if (em == NULL && me_eval == NULL) {
+ parent, &em, &vert_coords, use_rotation ? &vert_normals : nullptr);
+ if (em == nullptr && me_eval == nullptr) {
return;
}
- VertexDupliData_Params vdd_params = {
- .ctx = ctx,
- .use_rotation = use_rotation,
- };
+ VertexDupliData_Params vdd_params{ctx, use_rotation};
+
+ if (em != nullptr) {
+ VertexDupliData_EditMesh vdd{};
+ vdd.params = vdd_params;
+ vdd.em = em;
+ vdd.vert_coords = vert_coords;
+ vdd.vert_normals = vert_normals;
+ vdd.has_orco = (vert_coords != nullptr);
- if (em != NULL) {
- VertexDupliData_EditMesh vdd = {
- .params = vdd_params,
- .em = em,
- .vert_coords = vert_coords,
- .vert_normals = vert_normals,
- .has_orco = (vert_coords != NULL),
- };
make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_editmesh);
}
else {
- VertexDupliData_Mesh vdd = {
- .params = vdd_params,
- .totvert = me_eval->totvert,
- .mvert = me_eval->mvert,
- .orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO),
- };
+ VertexDupliData_Mesh vdd{};
+ vdd.params = vdd_params;
+ vdd.totvert = me_eval->totvert;
+ vdd.mvert = me_eval->mvert;
+ vdd.orco = (const float(*)[3])CustomData_get_layer(&me_eval->vdata, CD_ORCO);
+
make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_mesh);
}
}
@@ -620,34 +646,31 @@ static const DupliGenerator gen_dupli_verts = {
static Object *find_family_object(
Main *bmain, const char *family, size_t family_len, unsigned int ch, GHash *family_gh)
{
- Object **ob_pt;
- Object *ob;
void *ch_key = POINTER_FROM_UINT(ch);
+ Object **ob_pt;
if ((ob_pt = (Object **)BLI_ghash_lookup_p(family_gh, ch_key))) {
- ob = *ob_pt;
+ return *ob_pt;
}
- else {
- char ch_utf8[7];
- size_t ch_utf8_len;
- ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8);
- ch_utf8[ch_utf8_len] = '\0';
- ch_utf8_len += 1; /* Compare with null terminator. */
+ char ch_utf8[7];
+ size_t ch_utf8_len;
- for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- if (STREQLEN(ob->id.name + 2 + family_len, ch_utf8, ch_utf8_len)) {
- if (STREQLEN(ob->id.name + 2, family, family_len)) {
- break;
- }
+ ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8);
+ ch_utf8[ch_utf8_len] = '\0';
+ ch_utf8_len += 1; /* Compare with null terminator. */
+
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (STREQLEN(ob->id.name + 2 + family_len, ch_utf8, ch_utf8_len)) {
+ if (STREQLEN(ob->id.name + 2, family, family_len)) {
+ /* Inserted value can be nullptr, just to save searches in future. */
+ BLI_ghash_insert(family_gh, ch_key, ob);
+ return ob;
}
}
-
- /* Inserted value can be NULL, just to save searches in future. */
- BLI_ghash_insert(family_gh, ch_key, ob);
}
- return ob;
+ return nullptr;
}
static void make_duplis_font(const DupliContext *ctx)
@@ -656,11 +679,11 @@ static void make_duplis_font(const DupliContext *ctx)
GHash *family_gh;
Object *ob;
Curve *cu;
- struct CharTrans *ct, *chartransdata = NULL;
+ struct CharTrans *ct, *chartransdata = nullptr;
float vec[3], obmat[4][4], pmat[4][4], fsize, xof, yof;
int text_len, a;
size_t family_len;
- const char32_t *text = NULL;
+ const char32_t *text = nullptr;
bool text_free = false;
/* Font dupli-verts not supported inside collections. */
@@ -673,13 +696,13 @@ static void make_duplis_font(const DupliContext *ctx)
/* In `par` the family name is stored, use this to find the other objects. */
BKE_vfont_to_curve_ex(
- par, par->data, FO_DUPLI, NULL, &text, &text_len, &text_free, &chartransdata);
+ par, (Curve *)par->data, FO_DUPLI, nullptr, &text, &text_len, &text_free, &chartransdata);
- if (text == NULL || chartransdata == NULL) {
+ if (text == nullptr || chartransdata == nullptr) {
return;
}
- cu = par->data;
+ cu = (Curve *)par->data;
fsize = cu->fsize;
xof = cu->xof;
yof = cu->yof;
@@ -733,7 +756,7 @@ static void make_duplis_font(const DupliContext *ctx)
MEM_freeN((void *)text);
}
- BLI_ghash_free(family_gh, NULL, NULL);
+ BLI_ghash_free(family_gh, nullptr, nullptr);
MEM_freeN(chartransdata);
}
@@ -754,11 +777,11 @@ static void make_child_duplis_pointcloud(const DupliContext *ctx,
Object *child)
{
const Object *parent = ctx->object;
- const PointCloud *pointcloud = parent->data;
+ const PointCloud *pointcloud = (PointCloud *)parent->data;
const float(*co)[3] = pointcloud->co;
const float *radius = pointcloud->radius;
- const float(*rotation)[4] = NULL; /* TODO: add optional rotation attribute. */
- const float(*orco)[3] = NULL; /* TODO: add optional texture coordinate attribute. */
+ const float(*rotation)[4] = nullptr; /* TODO: add optional rotation attribute. */
+ const float(*orco)[3] = nullptr; /* TODO: add optional texture coordinate attribute. */
/* Relative transform from parent to child space. */
float child_imat[4][4];
@@ -797,7 +820,7 @@ static void make_child_duplis_pointcloud(const DupliContext *ctx,
static void make_duplis_pointcloud(const DupliContext *ctx)
{
- make_child_duplis(ctx, NULL, make_child_duplis_pointcloud);
+ make_child_duplis(ctx, nullptr, make_child_duplis_pointcloud);
}
static const DupliGenerator gen_dupli_verts_pointcloud = {
@@ -813,43 +836,44 @@ static const DupliGenerator gen_dupli_verts_pointcloud = {
static void make_duplis_instances_component(const DupliContext *ctx)
{
- float(*instance_offset_matrices)[4][4];
- InstancedData *instanced_data;
- const int *almost_unique_ids;
- const int amount = BKE_geometry_set_instances(ctx->object->runtime.geometry_set_eval,
- &instance_offset_matrices,
- &almost_unique_ids,
- &instanced_data);
+ const InstancesComponent *component =
+ ctx->object->runtime.geometry_set_eval->get_component_for_read<InstancesComponent>();
+ if (component == nullptr) {
+ return;
+ }
- for (int i = 0; i < amount; i++) {
- InstancedData *data = &instanced_data[i];
+ Span<float4x4> instance_offset_matrices = component->instance_transforms();
+ Span<int> instance_reference_handles = component->instance_reference_handles();
+ Span<int> almost_unique_ids = component->almost_unique_ids();
+ Span<InstanceReference> references = component->references();
+ for (int64_t i : instance_offset_matrices.index_range()) {
+ const InstanceReference &reference = references[instance_reference_handles[i]];
const int id = almost_unique_ids[i];
- if (data->type == INSTANCE_DATA_TYPE_OBJECT) {
- Object *object = data->data.object;
- if (object != NULL) {
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
float matrix[4][4];
- mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i]);
- make_dupli(ctx, object, matrix, id);
+ mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values);
+ make_dupli(ctx, &object, matrix, id);
float space_matrix[4][4];
- mul_m4_m4m4(space_matrix, instance_offset_matrices[i], object->imat);
+ mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat);
mul_m4_m4_pre(space_matrix, ctx->object->obmat);
- make_recursive_duplis(ctx, object, space_matrix, id);
+ make_recursive_duplis(ctx, &object, space_matrix, id);
+ break;
}
- }
- else if (data->type == INSTANCE_DATA_TYPE_COLLECTION) {
- Collection *collection = data->data.collection;
- if (collection != NULL) {
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
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_matrices[i]);
+ sub_v3_v3(collection_matrix[3], collection.instance_offset);
+ mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values);
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) {
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) {
if (object == ctx->object) {
continue;
}
@@ -861,6 +885,10 @@ static void make_duplis_instances_component(const DupliContext *ctx)
make_recursive_duplis(ctx, object, collection_matrix, id);
}
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
}
}
}
@@ -878,7 +906,7 @@ static const DupliGenerator gen_dupli_instances_component = {
* \{ */
/** Values shared between different mesh types. */
-typedef struct FaceDupliData_Params {
+struct FaceDupliData_Params {
/**
* It's important we use this context instead of the `ctx` passed into #make_child_duplis
* since these won't match in the case of recursion.
@@ -886,9 +914,9 @@ typedef struct FaceDupliData_Params {
const DupliContext *ctx;
bool use_scale;
-} FaceDupliData_Params;
+};
-typedef struct FaceDupliData_Mesh {
+struct FaceDupliData_Mesh {
FaceDupliData_Params params;
int totface;
@@ -897,53 +925,50 @@ typedef struct FaceDupliData_Mesh {
const MVert *mvert;
const float (*orco)[3];
const MLoopUV *mloopuv;
-} FaceDupliData_Mesh;
+};
-typedef struct FaceDupliData_EditMesh {
+struct FaceDupliData_EditMesh {
FaceDupliData_Params params;
BMEditMesh *em;
bool has_orco, has_uvs;
int cd_loop_uv_offset;
- /* Can be NULL. */
+ /* Can be nullptr. */
const float (*vert_coords)[3];
-} FaceDupliData_EditMesh;
+};
-static void get_dupliface_transform_from_coords(const float coords[][3],
- const int coords_len,
+static void get_dupliface_transform_from_coords(Span<float3> coords,
const bool use_scale,
const float scale_fac,
float r_mat[4][4])
{
- float loc[3], quat[4], scale, size[3];
-
/* Location. */
- {
- const float w = 1.0f / (float)coords_len;
- zero_v3(loc);
- for (int i = 0; i < coords_len; i++) {
- madd_v3_v3fl(loc, coords[i], w);
- }
+ float3 location(0);
+ for (const float3 &coord : coords) {
+ location += coord;
}
+ location *= 1.0f / (float)coords.size();
+
/* Rotation. */
- {
- float f_no[3];
- cross_poly_v3(f_no, coords, (uint)coords_len);
- normalize_v3(f_no);
- tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no);
- }
+ float quat[4];
+
+ float3 f_no;
+ cross_poly_v3(f_no, (const float(*)[3])coords.data(), (uint)coords.size());
+ f_no.normalize();
+ tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no);
+
/* Scale. */
+ float scale;
if (use_scale) {
- const float area = area_poly_v3(coords, (uint)coords_len);
+ const float area = area_poly_v3((const float(*)[3])coords.data(), (uint)coords.size());
scale = sqrtf(area) * scale_fac;
}
else {
scale = 1.0f;
}
- size[0] = size[1] = size[2] = scale;
- loc_quat_size_to_mat4(r_mat, loc, quat, size);
+ loc_quat_size_to_mat4(r_mat, location, quat, float3(scale));
}
static DupliObject *face_dupli(const DupliContext *ctx,
@@ -952,14 +977,13 @@ static DupliObject *face_dupli(const DupliContext *ctx,
const int index,
const bool use_scale,
const float scale_fac,
- const float (*coords)[3],
- const int coords_len)
+ Span<float3> coords)
{
float obmat[4][4];
float space_mat[4][4];
/* `obmat` is transform to face. */
- get_dupliface_transform_from_coords(coords, coords_len, use_scale, scale_fac, obmat);
+ get_dupliface_transform_from_coords(coords, use_scale, scale_fac, obmat);
/* Make offset relative to inst_ob using relative child transform. */
mul_mat3_m4_v3(child_imat, obmat[3]);
@@ -987,7 +1011,6 @@ static DupliObject *face_dupli(const DupliContext *ctx,
return dob;
}
-/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
static DupliObject *face_dupli_from_mesh(const DupliContext *ctx,
Object *inst_ob,
const float child_imat[4][4],
@@ -1001,17 +1024,16 @@ static DupliObject *face_dupli_from_mesh(const DupliContext *ctx,
const MVert *mvert)
{
const int coords_len = mpoly->totloop;
- float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len);
+ Array<float3, 64> coords(coords_len);
const MLoop *ml = mloopstart;
for (int i = 0; i < coords_len; i++, ml++) {
- copy_v3_v3(coords[i], mvert[ml->v].co);
+ coords[i] = float3(mvert[ml->v].co);
}
- return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
+ return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords);
}
-/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
Object *inst_ob,
const float child_imat[4][4],
@@ -1024,12 +1046,12 @@ static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
const float (*vert_coords)[3])
{
const int coords_len = f->len;
- float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len);
+ Array<float3, 64> coords(coords_len);
BMLoop *l_first, *l_iter;
int i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- if (vert_coords != NULL) {
+ if (vert_coords != nullptr) {
do {
copy_v3_v3(coords[i++], vert_coords[BM_elem_index_get(l_iter->v)]);
} while ((l_iter = l_iter->next) != l_first);
@@ -1040,14 +1062,14 @@ static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
} while ((l_iter = l_iter->next) != l_first);
}
- return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
+ return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords);
}
static void make_child_duplis_faces_from_mesh(const DupliContext *ctx,
void *userdata,
Object *inst_ob)
{
- FaceDupliData_Mesh *fdd = userdata;
+ FaceDupliData_Mesh *fdd = (FaceDupliData_Mesh *)userdata;
const MPoly *mpoly = fdd->mpoly, *mp;
const MLoop *mloop = fdd->mloop;
const MVert *mvert = fdd->mvert;
@@ -1087,7 +1109,7 @@ static void make_child_duplis_faces_from_editmesh(const DupliContext *ctx,
void *userdata,
Object *inst_ob)
{
- FaceDupliData_EditMesh *fdd = userdata;
+ FaceDupliData_EditMesh *fdd = (FaceDupliData_EditMesh *)userdata;
BMEditMesh *em = fdd->em;
float child_imat[4][4];
int a;
@@ -1097,7 +1119,7 @@ static void make_child_duplis_faces_from_editmesh(const DupliContext *ctx,
const float(*vert_coords)[3] = fdd->vert_coords;
- BLI_assert((vert_coords == NULL) || (em->bm->elem_index_dirty & BM_VERT) == 0);
+ BLI_assert((vert_coords == nullptr) || (em->bm->elem_index_dirty & BM_VERT) == 0);
invert_m4_m4(inst_ob->imat, inst_ob->obmat);
/* Relative transform from parent to child space. */
@@ -1127,44 +1149,41 @@ static void make_duplis_faces(const DupliContext *ctx)
Object *parent = ctx->object;
/* Gather mesh info. */
- BMEditMesh *em = NULL;
- const float(*vert_coords)[3] = NULL;
- Mesh *me_eval = mesh_data_from_duplicator_object(parent, &em, &vert_coords, NULL);
- if (em == NULL && me_eval == NULL) {
+ BMEditMesh *em = nullptr;
+ const float(*vert_coords)[3] = nullptr;
+ Mesh *me_eval = mesh_data_from_duplicator_object(parent, &em, &vert_coords, nullptr);
+ if (em == nullptr && me_eval == nullptr) {
return;
}
- FaceDupliData_Params fdd_params = {
- .ctx = ctx,
- .use_scale = parent->transflag & OB_DUPLIFACES_SCALE,
- };
+ FaceDupliData_Params fdd_params = {ctx, (parent->transflag & OB_DUPLIFACES_SCALE) != 0};
- if (em != NULL) {
+ if (em != nullptr) {
const int uv_idx = CustomData_get_render_layer(&em->bm->ldata, CD_MLOOPUV);
- FaceDupliData_EditMesh fdd = {
- .params = fdd_params,
- .em = em,
- .vert_coords = vert_coords,
- .has_orco = (vert_coords != NULL),
- .has_uvs = (uv_idx != -1),
- .cd_loop_uv_offset = (uv_idx != -1) ?
- CustomData_get_n_offset(&em->bm->ldata, CD_MLOOPUV, uv_idx) :
- -1,
- };
+ FaceDupliData_EditMesh fdd{};
+ fdd.params = fdd_params;
+ fdd.em = em;
+ fdd.vert_coords = vert_coords;
+ fdd.has_orco = (vert_coords != nullptr);
+ fdd.has_uvs = (uv_idx != -1);
+ fdd.cd_loop_uv_offset = (uv_idx != -1) ?
+ CustomData_get_n_offset(&em->bm->ldata, CD_MLOOPUV, uv_idx) :
+ -1;
make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_editmesh);
}
else {
const int uv_idx = CustomData_get_render_layer(&me_eval->ldata, CD_MLOOPUV);
- FaceDupliData_Mesh fdd = {
- .params = fdd_params,
- .totface = me_eval->totpoly,
- .mpoly = me_eval->mpoly,
- .mloop = me_eval->mloop,
- .mvert = me_eval->mvert,
- .mloopuv = (uv_idx != -1) ? CustomData_get_layer_n(&me_eval->ldata, CD_MLOOPUV, uv_idx) :
- NULL,
- .orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO),
- };
+ FaceDupliData_Mesh fdd{};
+ fdd.params = fdd_params;
+ fdd.totface = me_eval->totpoly;
+ fdd.mpoly = me_eval->mpoly;
+ fdd.mloop = me_eval->mloop;
+ fdd.mvert = me_eval->mvert;
+ fdd.mloopuv = (uv_idx != -1) ? (const MLoopUV *)CustomData_get_layer_n(
+ &me_eval->ldata, CD_MLOOPUV, uv_idx) :
+ nullptr;
+ fdd.orco = (const float(*)[3])CustomData_get_layer(&me_eval->vdata, CD_ORCO);
+
make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_mesh);
}
}
@@ -1187,12 +1206,11 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
bool for_render = mode == DAG_EVAL_RENDER;
- Object *ob = NULL, **oblist = NULL;
+ Object *ob = nullptr, **oblist = nullptr;
DupliObject *dob;
- ParticleDupliWeight *dw;
ParticleSettings *part;
ParticleData *pa;
- ChildParticle *cpa = NULL;
+ ChildParticle *cpa = nullptr;
ParticleKey state;
ParticleCacheKey *cache;
float ctime, scale = 1.0f;
@@ -1202,13 +1220,13 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
int no_draw_flag = PARS_UNEXIST;
- if (psys == NULL) {
+ if (psys == nullptr) {
return;
}
part = psys->part;
- if (part == NULL) {
+ if (part == nullptr) {
return;
}
@@ -1228,7 +1246,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
if ((for_render || part->draw_as == PART_DRAW_REND) &&
ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
- ParticleSimulationData sim = {NULL};
+ ParticleSimulationData sim = {nullptr};
sim.depsgraph = ctx->depsgraph;
sim.scene = scene;
sim.ob = par;
@@ -1239,12 +1257,12 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
/* First check for loops (particle system object used as dupli-object). */
if (part->ren_as == PART_DRAW_OB) {
- if (ELEM(part->instance_object, NULL, par)) {
+ if (ELEM(part->instance_object, nullptr, par)) {
return;
}
}
else { /* #PART_DRAW_GR. */
- if (part->instance_collection == NULL) {
+ if (part->instance_collection == nullptr) {
return;
}
@@ -1285,8 +1303,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
if (part->ren_as == PART_DRAW_GR) {
if (use_collection_count) {
psys_find_group_weights(part);
-
- for (dw = part->instance_weights.first; dw; dw = dw->next) {
+ LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
part->instance_collection, object, mode) {
if (dw->ob == object) {
@@ -1306,11 +1323,12 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
}
- oblist = MEM_callocN((size_t)totcollection * sizeof(Object *), "dupcollection object list");
+ oblist = (Object **)MEM_callocN((size_t)totcollection * sizeof(Object *),
+ "dupcollection object list");
if (use_collection_count) {
a = 0;
- for (dw = part->instance_weights.first; dw; dw = dw->next) {
+ LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
part->instance_collection, object, mode) {
if (dw->ob == object) {
@@ -1363,7 +1381,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
#if 0 /* UNUSED */
pa_num = a;
#endif
- size = psys_get_child_size(psys, cpa, ctime, NULL);
+ size = psys_get_child_size(psys, cpa, ctime, nullptr);
}
/* Some hair paths might be non-existent so they can't be used for duplication. */
@@ -1394,11 +1412,11 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
/* Hair we handle separate and compute transform based on hair keys. */
if (a < totpart) {
cache = psys->pathcache[a];
- psys_get_dupli_path_transform(&sim, pa, NULL, cache, pamat, &scale);
+ psys_get_dupli_path_transform(&sim, pa, nullptr, cache, pamat, &scale);
}
else {
cache = psys->childcache[a - totpart];
- psys_get_dupli_path_transform(&sim, NULL, cpa, cache, pamat, &scale);
+ psys_get_dupli_path_transform(&sim, nullptr, cpa, cache, pamat, &scale);
}
copy_v3_v3(pamat[3], cache->co);
@@ -1506,20 +1524,18 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
if (psys->lattice_deform_data) {
BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
- psys->lattice_deform_data = NULL;
+ psys->lattice_deform_data = nullptr;
}
}
static void make_duplis_particles(const DupliContext *ctx)
{
- ParticleSystem *psys;
- int psysid;
-
/* Particle system take up one level in id, the particles another. */
- for (psys = ctx->object->particlesystem.first, psysid = 0; psys; psys = psys->next, psysid++) {
+ int psysid;
+ LISTBASE_FOREACH_INDEX (ParticleSystem *, psys, &ctx->object->particlesystem, psysid) {
/* Particles create one more level for persistent `psys` index. */
DupliContext pctx;
- copy_dupli_context(&pctx, ctx, ctx->object, NULL, psysid);
+ copy_dupli_context(&pctx, ctx, ctx->object, nullptr, psysid);
make_duplis_particle_system(&pctx, psys);
}
}
@@ -1540,17 +1556,17 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
int transflag = ctx->object->transflag;
int restrictflag = ctx->object->restrictflag;
- if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == NULL) {
- return NULL;
+ if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == nullptr) {
+ return nullptr;
}
/* Should the dupli's be generated for this object? - Respect restrict flags. */
if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) :
(restrictflag & OB_RESTRICT_VIEWPORT)) {
- return NULL;
+ return nullptr;
}
- if (ctx->object->runtime.geometry_set_eval != NULL) {
+ if (ctx->object->runtime.geometry_set_eval != nullptr) {
if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) {
return &gen_dupli_instances_component;
}
@@ -1579,7 +1595,7 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
return &gen_dupli_collection;
}
- return NULL;
+ return nullptr;
}
/** \} */
@@ -1593,9 +1609,11 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
*/
ListBase *object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob)
{
- ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist");
+ ListBase *duplilist = (ListBase *)MEM_callocN(sizeof(ListBase), "duplilist");
DupliContext ctx;
- init_context(&ctx, depsgraph, sce, ob, NULL);
+ Vector<Object *> instance_stack;
+ instance_stack.append(ob);
+ init_context(&ctx, depsgraph, sce, ob, nullptr, instance_stack);
if (ctx.gen) {
ctx.duplilist = duplilist;
ctx.gen->make_duplis(&ctx);
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index b1ae4abd9bb..e6909127503 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -176,6 +176,13 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
CustomData_MeshMasks cddata_masks = scene->customdata_mask;
CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH);
+ /* Custom attributes should not be removed automatically. They might be used by the render
+ * engine or scripts. They can still be removed explicitly using geometry nodes. */
+ cddata_masks.vmask |= CD_MASK_PROP_ALL;
+ cddata_masks.emask |= CD_MASK_PROP_ALL;
+ cddata_masks.fmask |= CD_MASK_PROP_ALL;
+ cddata_masks.pmask |= CD_MASK_PROP_ALL;
+ cddata_masks.lmask |= CD_MASK_PROP_ALL;
/* Make sure Freestyle edge/face marks appear in DM for render (see T40315).
* Due to Line Art implementation, edge marks should also be shown in viewport. */
#ifdef WITH_FREESTYLE
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index d2f4d0702ed..9b9ed0adcf4 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -663,7 +663,7 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount
osd.scale = scale;
osd.chop_amount = chop_amount;
- pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH);
+ pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);
@@ -911,8 +911,12 @@ void BKE_ocean_init(struct Ocean *o,
for (i = 0; i < o->_M; i++) {
for (j = 0; j < o->_N; j++) {
/* This ensures we get a value tied to the surface location, avoiding dramatic surface
- * change with changing resolution. */
- int new_seed = seed + BLI_hash_int_2d(o->_kx[i] * 360.0f, o->_kz[j] * 360.0f);
+ * change with changing resolution.
+ * Explicitly cast to signed int first to ensure consistent behavior on all processors,
+ * since behavior of float to unsigned int cast is undefined in C. */
+ const int hash_x = o->_kx[i] * 360.0f;
+ const int hash_z = o->_kz[j] * 360.0f;
+ int new_seed = seed + BLI_hash_int_2d(hash_x, hash_z);
BLI_rng_seed(rng, new_seed);
float r1 = gaussRand(rng);
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index e50b321900a..3ae5d039125 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -101,6 +101,8 @@ static void particle_settings_init(ID *id)
MEMCPY_STRUCT_AFTER(particle_settings, DNA_struct_default_get(ParticleSettings), id);
particle_settings->effector_weights = BKE_effector_add_weights(NULL);
+ particle_settings->pd = BKE_partdeflect_new(PFIELD_NULL);
+ particle_settings->pd2 = BKE_partdeflect_new(PFIELD_NULL);
}
static void particle_settings_copy_data(Main *UNUSED(bmain),
@@ -2417,14 +2419,15 @@ int do_guides(Depsgraph *depsgraph,
cu = (Curve *)eff->ob->data;
if (pd->flag & PFIELD_GUIDE_PATH_ADD) {
- if (where_on_path(
+ if (BKE_where_on_path(
eff->ob, data->strength * guidetime, guidevec, guidedir, NULL, &radius, &weight) ==
0) {
return 0;
}
}
else {
- if (where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0) {
+ if (BKE_where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) ==
+ 0) {
return 0;
}
}
@@ -3176,7 +3179,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
return;
}
- task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
totchild = ctx.totchild;
totparent = ctx.totparent;
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index ad617b4198b..6cae6cd6fa2 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -1330,7 +1330,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
return;
}
- task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart);
psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks);
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index c727a144c87..149e345e501 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -3312,13 +3312,11 @@ static MDeformVert *hair_set_pinning(MDeformVert *dvert, float weight)
static void hair_create_input_mesh(ParticleSimulationData *sim,
int totpoint,
int totedge,
- Mesh **r_mesh,
- ClothHairData **r_hairdata)
+ Mesh **r_mesh)
{
ParticleSystem *psys = sim->psys;
ParticleSettings *part = psys->part;
Mesh *mesh;
- ClothHairData *hairdata;
MVert *mvert;
MEdge *medge;
MDeformVert *dvert;
@@ -3339,9 +3337,8 @@ static void hair_create_input_mesh(ParticleSimulationData *sim,
medge = mesh->medge;
dvert = mesh->dvert;
- hairdata = *r_hairdata;
- if (!hairdata) {
- *r_hairdata = hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data");
+ if (psys->clmd->hairdata == NULL) {
+ psys->clmd->hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data");
}
/* calculate maximum segment length */
@@ -3493,7 +3490,7 @@ static void do_hair_dynamics(ParticleSimulationData *sim)
}
}
- hair_create_input_mesh(sim, totpoint, totedge, &psys->hair_in_mesh, &psys->clmd->hairdata);
+ hair_create_input_mesh(sim, totpoint, totedge, &psys->hair_in_mesh);
if (psys->hair_out_mesh) {
BKE_id_free(NULL, psys->hair_out_mesh);
@@ -4912,9 +4909,12 @@ void particle_system_update(struct Depsgraph *depsgraph,
sim.psmd->flag |= eParticleSystemFlag_Pars;
}
+ ParticleTexture ptex;
+
LOOP_EXISTING_PARTICLES
{
- pa->size = part->size;
+ psys_get_texture(&sim, pa, &ptex, PAMAP_SIZE, cfra);
+ pa->size = part->size * ptex.size;
if (part->randsize > 0.0f) {
pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1);
}
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 63bc8753fc7..948b57578dc 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -187,31 +187,31 @@ int BB_widest_axis(const BB *bb);
void pbvh_grow_nodes(PBVH *bvh, int totnode);
bool ray_face_intersection_quad(const float ray_start[3],
struct IsectRayPrecalc *isect_precalc,
- const float *t0,
- const float *t1,
- const float *t2,
- const float *t3,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
+ const float t3[3],
float *depth);
bool ray_face_intersection_tri(const float ray_start[3],
struct IsectRayPrecalc *isect_precalc,
- const float *t0,
- const float *t1,
- const float *t2,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
float *depth);
bool ray_face_nearest_quad(const float ray_start[3],
const float ray_normal[3],
- const float *t0,
- const float *t1,
- const float *t2,
- const float *t3,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
+ const float t3[3],
float *r_depth,
float *r_dist_sq);
bool ray_face_nearest_tri(const float ray_start[3],
const float ray_normal[3],
- const float *t0,
- const float *t1,
- const float *t2,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
float *r_depth,
float *r_dist_sq);
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 17434ee8023..be206f8a642 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -1805,7 +1805,7 @@ int BKE_ptcache_mem_pointers_seek(int point_index, PTCacheMem *pm, void *cur[BPH
}
for (i = 0; i < BPHYS_TOT_DATA; i++) {
- cur[i] = data_types & (1 << i) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
+ cur[i] = (data_types & (1 << i)) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
}
return 1;
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 19078446009..21b86aa8148 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -260,10 +260,12 @@ static RigidBodyOb *rigidbody_copy_object(const Object *ob, const int flag)
RigidBodyOb *rboN = NULL;
if (ob->rigidbody_object) {
+ const bool is_orig = (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0;
+
/* just duplicate the whole struct first (to catch all the settings) */
rboN = MEM_dupallocN(ob->rigidbody_object);
- if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
+ if (is_orig) {
/* This is a regular copy, and not a CoW copy for depsgraph evaluation */
rboN->shared = MEM_callocN(sizeof(*rboN->shared), "RigidBodyOb_Shared");
}
@@ -1520,7 +1522,7 @@ void BKE_rigidbody_remove_object(Main *bmain, Scene *scene, Object *ob, const bo
if (rbw) {
/* remove object from array */
- if (rbw && rbw->objects) {
+ if (rbw->objects) {
for (i = 0; i < rbw->numbodies; i++) {
if (rbw->objects[i] == ob) {
rbw->objects[i] = NULL;
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index de3e1023b08..86d4c03d51a 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -742,7 +742,8 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS(data, view_layer->mat_override, IDWALK_CB_USER);
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- BKE_LIB_FOREACHID_PROCESS(data, base->object, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS(
+ data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
}
scene_foreach_layer_collection(data, &view_layer->layer_collections);
@@ -1984,8 +1985,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
const bool is_subprocess = false;
if (!is_subprocess) {
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
/* In case root duplicated ID is linked, assume we want to get a local copy of it and
* duplicate all expected linked data. */
if (ID_IS_LINKED(sce)) {
@@ -2026,8 +2026,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
#endif
/* Cleanup. */
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_collection_sync(bmain);
}
@@ -2517,6 +2516,18 @@ int BKE_scene_orientation_slot_get_index(const TransformOrientationSlot *orient_
orient_slot->type;
}
+int BKE_scene_orientation_get_index(Scene *scene, int slot_index)
+{
+ TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, slot_index);
+ return BKE_scene_orientation_slot_get_index(orient_slot);
+}
+
+int BKE_scene_orientation_get_index_from_flag(Scene *scene, int flag)
+{
+ TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(scene, flag);
+ return BKE_scene_orientation_slot_get_index(orient_slot);
+}
+
/** \} */
static bool check_rendered_viewport_visible(Main *bmain)
@@ -2624,6 +2635,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ bool used_multiple_passes = false;
bool run_callbacks = DEG_id_type_any_updated(depsgraph);
if (run_callbacks) {
@@ -2648,7 +2660,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
bmain, &scene->id, depsgraph, BKE_CB_EVT_DEPSGRAPH_UPDATE_POST);
/* It is possible that the custom callback modified scene and removed some IDs from the main
- * database. In this case DEG_ids_clear_recalc() will crash because it iterates over all IDs
+ * database. In this case DEG_editors_update() will crash because it iterates over all IDs
* which depsgraph was built for.
*
* The solution is to update relations prior to this call, avoiding access to freed IDs.
@@ -2660,10 +2672,6 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
* If there are no relations changed by the callback this call will do nothing. */
DEG_graph_relations_update(depsgraph);
}
- /* Inform editors about possible changes. */
- DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, false);
- /* Clear recalc flags. */
- DEG_ids_clear_recalc(bmain, depsgraph);
/* If user callback did not tag anything for update we can skip second iteration.
* Otherwise we update scene once again, but without running callbacks to bring
@@ -2672,8 +2680,22 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
break;
}
+ /* Clear recalc flags for second pass, but back them up for editors update. */
+ const bool backup = true;
+ DEG_ids_clear_recalc(depsgraph, backup);
+ used_multiple_passes = true;
run_callbacks = false;
}
+
+ /* Inform editors about changes, using recalc flags from both passes. */
+ if (used_multiple_passes) {
+ DEG_ids_restore_recalc(depsgraph);
+ }
+ const bool is_time_update = false;
+ DEG_editors_update(depsgraph, is_time_update);
+
+ const bool backup = false;
+ DEG_ids_clear_recalc(depsgraph, backup);
}
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
@@ -2687,11 +2709,11 @@ void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
}
/* applies changes right away, does all sets too */
-void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
+void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool clear_recalc)
{
Scene *scene = DEG_get_input_scene(depsgraph);
- ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
Main *bmain = DEG_get_bmain(depsgraph);
+ bool used_multiple_passes = false;
/* Keep this first. */
BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_PRE);
@@ -2724,24 +2746,44 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
BKE_callback_exec_id_depsgraph(bmain, &scene->id, depsgraph, BKE_CB_EVT_FRAME_CHANGE_POST);
/* NOTE: Similar to this case in scene_graph_update_tagged(). Need to ensure that
- * DEG_ids_clear_recalc() doesn't access freed memory of possibly removed ID. */
+ * DEG_editors_update() doesn't access freed memory of possibly removed ID. */
DEG_graph_relations_update(depsgraph);
}
- /* Inform editors about possible changes. */
- DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true);
- /* clear recalc flags */
- DEG_ids_clear_recalc(bmain, depsgraph);
-
/* If user callback did not tag anything for update we can skip second iteration.
* Otherwise we update scene once again, but without running callbacks to bring
* scene to a fully evaluated state with user modifications taken into account. */
if (DEG_is_fully_evaluated(depsgraph)) {
break;
}
+
+ /* Clear recalc flags for second pass, but back them up for editors update. */
+ const bool backup = true;
+ DEG_ids_clear_recalc(depsgraph, backup);
+ used_multiple_passes = true;
+ }
+
+ /* Inform editors about changes, using recalc flags from both passes. */
+ if (used_multiple_passes) {
+ DEG_ids_restore_recalc(depsgraph);
+ }
+
+ const bool is_time_update = true;
+ DEG_editors_update(depsgraph, is_time_update);
+
+ /* Clear recalc flags, can be skipped for e.g. renderers that will read these
+ * and clear the flags later. */
+ if (clear_recalc) {
+ const bool backup = false;
+ DEG_ids_clear_recalc(depsgraph, backup);
}
}
+void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
+{
+ BKE_scene_graph_update_for_newframe_ex(depsgraph, true);
+}
+
/**
* Ensures given scene/view_layer pair has a valid, up-to-date depsgraph.
*
@@ -3450,6 +3492,9 @@ static Depsgraph **scene_ensure_depsgraph_p(Main *bmain, Scene *scene, ViewLayer
BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name);
DEG_debug_name_set(*depsgraph_ptr, name);
+ /* These viewport depsgraphs communicate changes to the editors. */
+ DEG_enable_editors_update(*depsgraph_ptr);
+
return depsgraph_ptr;
}
@@ -3496,8 +3541,8 @@ GHash *BKE_scene_undo_depsgraphs_extract(Main *bmain)
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
if (scene->depsgraph_hash == NULL) {
- /* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will be
- * built so this pointer may be NULL. */
+ /* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will
+ * be built so this pointer may be NULL. */
continue;
}
for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL;
@@ -3742,9 +3787,7 @@ void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene)
SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->scene_sound == NULL) {
if (seq->sound != NULL) {
- if (seq->scene_sound == NULL) {
- seq->scene_sound = BKE_sound_add_scene_sound_defaults(scene, seq);
- }
+ seq->scene_sound = BKE_sound_add_scene_sound_defaults(scene, seq);
}
else if (seq->type == SEQ_TYPE_SCENE) {
if (seq->scene != NULL) {
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 1766ac5b85f..d0d63192ebf 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -227,7 +227,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
case SPACE_SPREADSHEET: {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- BKE_LIB_FOREACHID_PROCESS_ID(data, sspreadsheet->pinned_id, IDWALK_CB_NOP);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ BKE_LIB_FOREACHID_PROCESS(
+ data, ((SpreadsheetContextObject *)context)->object, IDWALK_CB_NOP);
+ }
+ }
break;
}
default:
@@ -1350,6 +1355,34 @@ static void write_area(BlendWriter *writer, ScrArea *area)
}
else if (sl->spacetype == SPACE_SPREADSHEET) {
BLO_write_struct(writer, SpaceSpreadsheet, sl);
+
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ BLO_write_struct(writer, SpreadsheetColumn, column);
+ BLO_write_struct(writer, SpreadsheetColumnID, column->id);
+ BLO_write_string(writer, column->id->name);
+ }
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ BLO_write_struct(writer, SpreadsheetContextObject, object_context);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context;
+ BLO_write_struct(writer, SpreadsheetContextModifier, modifier_context);
+ BLO_write_string(writer, modifier_context->modifier_name);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context;
+ BLO_write_struct(writer, SpreadsheetContextNode, node_context);
+ BLO_write_string(writer, node_context->node_name);
+ break;
+ }
+ }
+ }
}
}
}
@@ -1527,9 +1560,6 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
-
- v3d->flag |= V3D_INVALID_BACKBUF;
-
if (v3d->gpd) {
BLO_read_data_address(reader, &v3d->gpd);
BKE_gpencil_blend_read_data(reader, v3d->gpd);
@@ -1705,6 +1735,31 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
sspreadsheet->runtime = NULL;
+
+ BLO_read_list(reader, &sspreadsheet->columns);
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ BLO_read_data_address(reader, &column->id);
+ BLO_read_data_address(reader, &column->id->name);
+ }
+
+ BLO_read_list(reader, &sspreadsheet->context_path);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_NODE: {
+ SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context;
+ BLO_read_data_address(reader, &node_context->node_name);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context;
+ BLO_read_data_address(reader, &modifier_context->modifier_name);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ break;
+ }
+ }
+ }
}
}
@@ -1921,7 +1976,12 @@ void BKE_screen_area_blend_read_lib(BlendLibReader *reader, ID *parent_id, ScrAr
}
case SPACE_SPREADSHEET: {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- BLO_read_id_address(reader, parent_id->lib, &sspreadsheet->pinned_id);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ BLO_read_id_address(
+ reader, parent_id->lib, &((SpreadsheetContextObject *)context)->object);
+ }
+ }
break;
}
default:
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index de88e8a941c..fcc1afbc59b 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -499,7 +499,6 @@ static void ccd_mesh_free(ccd_Mesh *ccdm)
}
MEM_freeN(ccdm->mima);
MEM_freeN(ccdm);
- ccdm = NULL;
}
}
@@ -830,13 +829,13 @@ static void calculate_collision_balls(Object *ob)
}
/* creates new softbody if didn't exist yet, makes new points and springs arrays */
-static void renew_softbody(Scene *scene, Object *ob, int totpoint, int totspring)
+static void renew_softbody(Object *ob, int totpoint, int totspring)
{
SoftBody *sb;
int i;
short softflag;
if (ob->soft == NULL) {
- ob->soft = sbNew(scene);
+ ob->soft = sbNew();
}
else {
free_softbody_intern(ob->soft);
@@ -2680,7 +2679,7 @@ static void springs_from_mesh(Object *ob)
}
/* makes totally fresh start situation */
-static void mesh_to_softbody(Scene *scene, Object *ob)
+static void mesh_to_softbody(Object *ob)
{
SoftBody *sb;
Mesh *me = ob->data;
@@ -2698,7 +2697,7 @@ static void mesh_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, me->totvert, totedge);
+ renew_softbody(ob, me->totvert, totedge);
/* we always make body points */
sb = ob->soft;
@@ -2910,7 +2909,7 @@ static void makelatticesprings(Lattice *lt, BodySpring *bs, int dostiff, Object
}
/* makes totally fresh start situation */
-static void lattice_to_softbody(Scene *scene, Object *ob)
+static void lattice_to_softbody(Object *ob)
{
Lattice *lt = ob->data;
SoftBody *sb;
@@ -2930,7 +2929,7 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, totvert, totspring);
+ renew_softbody(ob, totvert, totspring);
sb = ob->soft; /* can be created in renew_softbody() */
bp = sb->bpoint;
@@ -2973,7 +2972,7 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
}
/* makes totally fresh start situation */
-static void curve_surf_to_softbody(Scene *scene, Object *ob)
+static void curve_surf_to_softbody(Object *ob)
{
Curve *cu = ob->data;
SoftBody *sb;
@@ -2994,7 +2993,7 @@ static void curve_surf_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, totvert, totspring);
+ renew_softbody(ob, totvert, totspring);
sb = ob->soft; /* can be created in renew_softbody() */
/* set vars now */
@@ -3118,7 +3117,7 @@ static void sb_new_scratch(SoftBody *sb)
/* ************ Object level, exported functions *************** */
/* allocates and initializes general main data */
-SoftBody *sbNew(Scene *scene)
+SoftBody *sbNew(void)
{
SoftBody *sb;
@@ -3141,12 +3140,6 @@ SoftBody *sbNew(Scene *scene)
/*todo backward file compat should copy inspring to inpush while reading old files*/
sb->inpush = 0.5f;
- sb->interval = 10;
- if (scene != NULL) {
- sb->sfra = scene->r.sfra;
- sb->efra = scene->r.efra;
- }
-
sb->colball = 0.49f;
sb->balldamp = 0.50f;
sb->ballstiff = 1.0f;
@@ -3181,9 +3174,11 @@ void sbFree(Object *ob)
return;
}
+ const bool is_orig = (ob->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0;
+
free_softbody_intern(sb);
- if ((ob->id.tag & LIB_TAG_NO_MAIN) == 0) {
+ if (is_orig) {
/* Only free shared data on non-CoW copies */
BKE_ptcache_free_list(&sb->shared->ptcaches);
sb->shared->pointcache = NULL;
@@ -3576,17 +3571,17 @@ void sbObjectStep(struct Depsgraph *depsgraph,
switch (ob->type) {
case OB_MESH:
- mesh_to_softbody(scene, ob);
+ mesh_to_softbody(ob);
break;
case OB_LATTICE:
- lattice_to_softbody(scene, ob);
+ lattice_to_softbody(ob);
break;
case OB_CURVE:
case OB_SURF:
- curve_surf_to_softbody(scene, ob);
+ curve_surf_to_softbody(ob);
break;
default:
- renew_softbody(scene, ob, numVerts, 0);
+ renew_softbody(ob, numVerts, 0);
break;
}
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
new file mode 100644
index 00000000000..2781ff1e49a
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -0,0 +1,380 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_spline.hh"
+
+#include "FN_generic_virtual_array.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_Typed;
+using blender::fn::GVArrayPtr;
+
+Spline::Type Spline::type() const
+{
+ return type_;
+}
+
+void Spline::translate(const blender::float3 &translation)
+{
+ for (float3 &position : this->positions()) {
+ position += translation;
+ }
+ this->mark_cache_invalid();
+}
+
+void Spline::transform(const blender::float4x4 &matrix)
+{
+ for (float3 &position : this->positions()) {
+ position = matrix * position;
+ }
+ this->mark_cache_invalid();
+}
+
+int Spline::evaluated_edges_size() const
+{
+ const int eval_size = this->evaluated_points_size();
+ if (eval_size == 1) {
+ return 0;
+ }
+
+ return this->is_cyclic_ ? eval_size : eval_size - 1;
+}
+
+float Spline::length() const
+{
+ Span<float> lengths = this->evaluated_lengths();
+ return (lengths.size() == 0) ? 0 : this->evaluated_lengths().last();
+}
+
+int Spline::segments_size() const
+{
+ const int points_len = this->size();
+
+ return is_cyclic_ ? points_len : points_len - 1;
+}
+
+bool Spline::is_cyclic() const
+{
+ return is_cyclic_;
+}
+
+void Spline::set_cyclic(const bool value)
+{
+ is_cyclic_ = value;
+}
+
+static void accumulate_lengths(Span<float3> positions,
+ const bool is_cyclic,
+ MutableSpan<float> lengths)
+{
+ float length = 0.0f;
+ for (const int i : IndexRange(positions.size() - 1)) {
+ length += float3::distance(positions[i], positions[i + 1]);
+ lengths[i] = length;
+ }
+ if (is_cyclic) {
+ lengths.last() = length + float3::distance(positions.last(), positions.first());
+ }
+}
+
+/**
+ * Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
+ * length of the subsequent segment, i.e. the first value is the length of the first segment rather
+ * than 0. This calculation is rather trivial, and only depends on the evaluated positions.
+ * However, the results are used often, so it makes sense to cache it.
+ */
+Span<float> Spline::evaluated_lengths() const
+{
+ if (!length_cache_dirty_) {
+ return evaluated_lengths_cache_;
+ }
+
+ std::lock_guard lock{length_cache_mutex_};
+ if (!length_cache_dirty_) {
+ return evaluated_lengths_cache_;
+ }
+
+ const int total = evaluated_edges_size();
+ evaluated_lengths_cache_.resize(total);
+
+ Span<float3> positions = this->evaluated_positions();
+ accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
+
+ length_cache_dirty_ = false;
+ return evaluated_lengths_cache_;
+}
+
+static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
+{
+ const float3 dir_prev = (middle - prev).normalized();
+ const float3 dir_next = (next - middle).normalized();
+
+ return (dir_prev + dir_next).normalized();
+}
+
+static void calculate_tangents(Span<float3> positions,
+ const bool is_cyclic,
+ MutableSpan<float3> tangents)
+{
+ if (positions.size() == 1) {
+ return;
+ }
+
+ for (const int i : IndexRange(1, positions.size() - 2)) {
+ tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
+ }
+
+ if (is_cyclic) {
+ const float3 &second_to_last = positions[positions.size() - 2];
+ const float3 &last = positions.last();
+ const float3 &first = positions.first();
+ const float3 &second = positions[1];
+ tangents.first() = direction_bisect(last, first, second);
+ tangents.last() = direction_bisect(second_to_last, last, first);
+ }
+ else {
+ tangents.first() = (positions[1] - positions[0]).normalized();
+ tangents.last() = (positions.last() - positions[positions.size() - 2]).normalized();
+ }
+}
+
+/**
+ * Return non-owning access to the direction of the curve at each evaluated point.
+ */
+Span<float3> Spline::evaluated_tangents() const
+{
+ if (!tangent_cache_dirty_) {
+ return evaluated_tangents_cache_;
+ }
+
+ std::lock_guard lock{tangent_cache_mutex_};
+ if (!tangent_cache_dirty_) {
+ return evaluated_tangents_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_tangents_cache_.resize(eval_size);
+
+ Span<float3> positions = this->evaluated_positions();
+
+ if (eval_size == 1) {
+ evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f);
+ }
+ else {
+ calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_);
+ this->correct_end_tangents();
+ }
+
+ tangent_cache_dirty_ = false;
+ return evaluated_tangents_cache_;
+}
+
+static float3 rotate_direction_around_axis(const float3 &direction,
+ const float3 &axis,
+ const float angle)
+{
+ BLI_ASSERT_UNIT_V3(direction);
+ BLI_ASSERT_UNIT_V3(axis);
+
+ const float3 axis_scaled = axis * float3::dot(direction, axis);
+ const float3 diff = direction - axis_scaled;
+ const float3 cross = float3::cross(axis, diff);
+
+ return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
+}
+
+static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
+{
+ for (const int i : normals.index_range()) {
+ normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
+ }
+}
+
+/**
+ * Return non-owning access to the direction vectors perpendicular to the tangents at every
+ * evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
+ */
+Span<float3> Spline::evaluated_normals() const
+{
+ if (!normal_cache_dirty_) {
+ return evaluated_normals_cache_;
+ }
+
+ std::lock_guard lock{normal_cache_mutex_};
+ if (!normal_cache_dirty_) {
+ return evaluated_normals_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_normals_cache_.resize(eval_size);
+
+ Span<float3> tangents = evaluated_tangents();
+ MutableSpan<float3> normals = evaluated_normals_cache_;
+
+ /* Only Z up normals are supported at the moment. */
+ calculate_normals_z_up(tangents, normals);
+
+ /* Rotate the generated normals with the interpolated tilt data. */
+ GVArray_Typed<float> tilts = this->interpolate_to_evaluated_points(this->tilts());
+ for (const int i : normals.index_range()) {
+ normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
+ }
+
+ normal_cache_dirty_ = false;
+ return evaluated_normals_cache_;
+}
+
+Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
+{
+ return this->lookup_evaluated_length(this->length() * factor);
+}
+
+/**
+ * \note This does not support extrapolation currently.
+ */
+Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
+{
+ BLI_assert(length >= 0.0f && length <= this->length());
+
+ Span<float> lengths = this->evaluated_lengths();
+
+ const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
+ const int index = offset - lengths.begin();
+ const int next_index = (index == this->size() - 1) ? 0 : index + 1;
+
+ const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
+ const float factor = (length - previous_length) / (lengths[index] - previous_length);
+
+ return LookupResult{index, next_index, factor};
+}
+
+/**
+ * Return an array of evenly spaced samples along the length of the spline. The samples are indices
+ * and factors to the next index encoded in floats. The logic for converting from the float values
+ * to interpolation data is in #lookup_data_from_index_factor.
+ */
+Array<float> Spline::sample_uniform_index_factors(const int samples_size) const
+{
+ const Span<float> lengths = this->evaluated_lengths();
+
+ BLI_assert(samples_size > 0);
+ Array<float> samples(samples_size);
+
+ samples[0] = 0.0f;
+ if (samples_size == 1) {
+ return samples;
+ }
+
+ const float total_length = this->length();
+ const float sample_length = total_length / (samples_size - (is_cyclic_ ? 0 : 1));
+
+ /* Store the length at the previous evaluated point in a variable so it can
+ * start out at zero (the lengths array doesn't contain 0 for the first point). */
+ float prev_length = 0.0f;
+ int i_sample = 1;
+ for (const int i_evaluated : IndexRange(this->evaluated_edges_size())) {
+ const float length = lengths[i_evaluated];
+
+ /* Add every sample that fits in this evaluated edge. */
+ while ((sample_length * i_sample) < length && i_sample < samples_size) {
+ const float factor = (sample_length * i_sample - prev_length) / (length - prev_length);
+ samples[i_sample] = i_evaluated + factor;
+ i_sample++;
+ }
+
+ prev_length = length;
+ }
+
+ if (!is_cyclic_) {
+ /* In rare cases this can prevent overflow of the stored index. */
+ samples.last() = lengths.size();
+ }
+
+ return samples;
+}
+
+Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
+{
+ const int points_len = this->evaluated_points_size();
+
+ if (is_cyclic_) {
+ if (index_factor < points_len) {
+ const int index = std::floor(index_factor);
+ const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ return LookupResult{index, next_index, index_factor - index};
+ }
+ return LookupResult{points_len - 1, 0, 1.0f};
+ }
+
+ if (index_factor < points_len - 1) {
+ const int index = std::floor(index_factor);
+ const int next_index = index + 1;
+ return LookupResult{index, next_index, index_factor - index};
+ }
+ return LookupResult{points_len - 2, points_len - 1, 1.0f};
+}
+
+void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
+{
+ Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
+ for (const float3 &position : positions) {
+ minmax_v3v3_v3(min, max, position);
+ }
+}
+
+GVArrayPtr Spline::interpolate_to_evaluated_points(GSpan data) const
+{
+ return this->interpolate_to_evaluated_points(GVArray_For_GSpan(data));
+}
+
+/**
+ * Sample any input data with a value for each evaluated point (already interpolated to evaluated
+ * points) to arbitrary parameters in between the evaluated points. The interpolation is quite
+ * simple, but this handles the cyclic and end point special cases.
+ */
+void Spline::sample_based_on_index_factors(const GVArray &src,
+ Span<float> index_factors,
+ GMutableSpan dst) const
+{
+ BLI_assert(src.size() == this->evaluated_points_size());
+
+ blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ const GVArray_Typed<T> src_typed = src.typed<T>();
+ MutableSpan<T> dst_typed = dst.typed<T>();
+ blender::parallel_for(dst_typed.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const LookupResult interp = this->lookup_data_from_index_factor(index_factors[i]);
+ dst_typed[i] = blender::attribute_math::mix2(interp.factor,
+ src_typed[interp.evaluated_index],
+ src_typed[interp.next_evaluated_index]);
+ }
+ });
+ });
+}
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
new file mode 100644
index 00000000000..3e421dcfc13
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -0,0 +1,592 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr BezierSpline::copy() const
+{
+ return std::make_unique<BezierSpline>(*this);
+}
+
+SplinePtr BezierSpline::copy_settings() const
+{
+ std::unique_ptr<BezierSpline> copy = std::make_unique<BezierSpline>();
+ copy_base_settings(*this, *copy);
+ copy->resolution_ = resolution_;
+ return copy;
+}
+
+int BezierSpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == handle_types_left_.size());
+ BLI_assert(size == handle_positions_left_.size());
+ BLI_assert(size == handle_types_right_.size());
+ BLI_assert(size == handle_positions_right_.size());
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ return size;
+}
+
+int BezierSpline::resolution() const
+{
+ return resolution_;
+}
+
+void BezierSpline::set_resolution(const int value)
+{
+ BLI_assert(value > 0);
+ resolution_ = value;
+ this->mark_cache_invalid();
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void BezierSpline::add_point(const float3 position,
+ const HandleType handle_type_left,
+ const float3 handle_position_left,
+ const HandleType handle_type_right,
+ const float3 handle_position_right,
+ const float radius,
+ const float tilt)
+{
+ handle_types_left_.append(handle_type_left);
+ handle_positions_left_.append(handle_position_left);
+ positions_.append(position);
+ handle_types_right_.append(handle_type_right);
+ handle_positions_right_.append(handle_position_right);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::resize(const int size)
+{
+ handle_types_left_.resize(size);
+ handle_positions_left_.resize(size);
+ positions_.resize(size);
+ handle_types_right_.resize(size);
+ handle_positions_right_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> BezierSpline::positions()
+{
+ return positions_;
+}
+Span<float3> BezierSpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> BezierSpline::radii()
+{
+ return radii_;
+}
+Span<float> BezierSpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> BezierSpline::tilts()
+{
+ return tilts_;
+}
+Span<float> BezierSpline::tilts() const
+{
+ return tilts_;
+}
+Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const
+{
+ return handle_types_left_;
+}
+MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left()
+{
+ return handle_types_left_;
+}
+Span<float3> BezierSpline::handle_positions_left() const
+{
+ this->ensure_auto_handles();
+ return handle_positions_left_;
+}
+MutableSpan<float3> BezierSpline::handle_positions_left()
+{
+ this->ensure_auto_handles();
+ return handle_positions_left_;
+}
+Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const
+{
+ return handle_types_right_;
+}
+MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right()
+{
+ return handle_types_right_;
+}
+Span<float3> BezierSpline::handle_positions_right() const
+{
+ this->ensure_auto_handles();
+ return handle_positions_right_;
+}
+MutableSpan<float3> BezierSpline::handle_positions_right()
+{
+ this->ensure_auto_handles();
+ return handle_positions_right_;
+}
+
+static float3 previous_position(Span<float3> positions, const bool cyclic, const int i)
+{
+ if (i == 0) {
+ if (cyclic) {
+ return positions[positions.size() - 1];
+ }
+ return 2.0f * positions[i] - positions[i + 1];
+ }
+ return positions[i - 1];
+}
+
+static float3 next_position(Span<float3> positions, const bool cyclic, const int i)
+{
+ if (i == positions.size() - 1) {
+ if (cyclic) {
+ return positions[0];
+ }
+ return 2.0f * positions[i] - positions[i - 1];
+ }
+ return positions[i + 1];
+}
+
+/**
+ * Recalculate all #Auto and #Vector handles with positions automatically
+ * derived from the neighboring control points.
+ */
+void BezierSpline::ensure_auto_handles() const
+{
+ if (!auto_handles_dirty_) {
+ return;
+ }
+
+ std::lock_guard lock{auto_handle_mutex_};
+ if (!auto_handles_dirty_) {
+ return;
+ }
+
+ for (const int i : IndexRange(this->size())) {
+ if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) {
+ const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i);
+ const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i];
+ float prev_len = prev_diff.length();
+ float next_len = next_diff.length();
+ if (prev_len == 0.0f) {
+ prev_len = 1.0f;
+ }
+ if (next_len == 0.0f) {
+ next_len = 1.0f;
+ }
+ const float3 dir = next_diff / next_len + prev_diff / prev_len;
+
+ /* This magic number is unfortunate, but comes from elsewhere in Blender. */
+ const float len = dir.length() * 2.5614f;
+ if (len != 0.0f) {
+ if (handle_types_left_[i] == HandleType::Auto) {
+ const float prev_len_clamped = std::min(prev_len, next_len * 5.0f);
+ handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len);
+ }
+ if (handle_types_right_[i] == HandleType::Auto) {
+ const float next_len_clamped = std::min(next_len, prev_len * 5.0f);
+ handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len);
+ }
+ }
+ }
+
+ if (handle_types_left_[i] == HandleType::Vector) {
+ const float3 prev = previous_position(positions_, is_cyclic_, i);
+ handle_positions_left_[i] = float3::interpolate(positions_[i], prev, 1.0f / 3.0f);
+ }
+
+ if (handle_types_right_[i] == HandleType::Vector) {
+ const float3 next = next_position(positions_, is_cyclic_, i);
+ handle_positions_right_[i] = float3::interpolate(positions_[i], next, 1.0f / 3.0f);
+ }
+ }
+
+ auto_handles_dirty_ = false;
+}
+
+void BezierSpline::translate(const blender::float3 &translation)
+{
+ for (float3 &position : this->positions()) {
+ position += translation;
+ }
+ for (float3 &handle_position : this->handle_positions_left()) {
+ handle_position += translation;
+ }
+ for (float3 &handle_position : this->handle_positions_right()) {
+ handle_position += translation;
+ }
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::transform(const blender::float4x4 &matrix)
+{
+ for (float3 &position : this->positions()) {
+ position = matrix * position;
+ }
+ for (float3 &handle_position : this->handle_positions_left()) {
+ handle_position = matrix * handle_position;
+ }
+ for (float3 &handle_position : this->handle_positions_right()) {
+ handle_position = matrix * handle_position;
+ }
+ this->mark_cache_invalid();
+}
+
+bool BezierSpline::point_is_sharp(const int index) const
+{
+ return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) ||
+ ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free);
+}
+
+bool BezierSpline::segment_is_vector(const int index) const
+{
+ if (index == this->size() - 1) {
+ if (is_cyclic_) {
+ return handle_types_right_.last() == HandleType::Vector &&
+ handle_types_left_.first() == HandleType::Vector;
+ }
+ /* There is actually no segment in this case, but it's nice to avoid
+ * having a special case for the last segment in calling code. */
+ return true;
+ }
+ return handle_types_right_[index] == HandleType::Vector &&
+ handle_types_left_[index + 1] == HandleType::Vector;
+}
+
+void BezierSpline::mark_cache_invalid()
+{
+ offset_cache_dirty_ = true;
+ position_cache_dirty_ = true;
+ mapping_cache_dirty_ = true;
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+ auto_handles_dirty_ = true;
+}
+
+int BezierSpline::evaluated_points_size() const
+{
+ BLI_assert(this->size() > 0);
+ return this->control_point_offsets().last();
+}
+
+/**
+ * If the spline is not cyclic, the direction for the first and last points is just the
+ * direction formed by the corresponding handles and control points. In the unlikely situation
+ * that the handles define a zero direction, fallback to using the direction defined by the
+ * first and last evaluated segments already calculated in #Spline::evaluated_tangents().
+ */
+void BezierSpline::correct_end_tangents() const
+{
+ if (is_cyclic_) {
+ return;
+ }
+
+ MutableSpan<float3> tangents(evaluated_tangents_cache_);
+
+ if (handle_positions_right_.first() != positions_.first()) {
+ tangents.first() = (handle_positions_right_.first() - positions_.first()).normalized();
+ }
+ if (handle_positions_left_.last() != positions_.last()) {
+ tangents.last() = (positions_.last() - handle_positions_left_.last()).normalized();
+ }
+}
+
+static void bezier_forward_difference_3d(const float3 &point_0,
+ const float3 &point_1,
+ const float3 &point_2,
+ const float3 &point_3,
+ MutableSpan<float3> result)
+{
+ BLI_assert(result.size() > 0);
+ const float inv_len = 1.0f / static_cast<float>(result.size());
+ const float inv_len_squared = inv_len * inv_len;
+ const float inv_len_cubed = inv_len_squared * inv_len;
+
+ const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len;
+ const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared;
+ const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed;
+
+ float3 q0 = point_0;
+ float3 q1 = rt1 + rt2 + rt3;
+ float3 q2 = 2.0f * rt2 + 6.0f * rt3;
+ float3 q3 = 6.0f * rt3;
+ for (const int i : result.index_range()) {
+ result[i] = q0;
+ q0 += q1;
+ q1 += q2;
+ q2 += q3;
+ }
+}
+
+void BezierSpline::evaluate_bezier_segment(const int index,
+ const int next_index,
+ MutableSpan<float3> positions) const
+{
+ if (this->segment_is_vector(index)) {
+ BLI_assert(positions.size() == 1);
+ positions.first() = positions_[index];
+ }
+ else {
+ bezier_forward_difference_3d(positions_[index],
+ handle_positions_right_[index],
+ handle_positions_left_[next_index],
+ positions_[next_index],
+ positions);
+ }
+}
+
+/**
+ * Returns access to a cache of offsets into the evaluated point array for each control point.
+ * While most control point edges generate the number of edges specified by the resolution, vector
+ * segments only generate one edge.
+ *
+ * \note The length of the result is one greater than the number of points, so that the last item
+ * is the total number of evaluated points. This is useful to avoid recalculating the size of the
+ * last segment everywhere.
+ */
+Span<int> BezierSpline::control_point_offsets() const
+{
+ if (!offset_cache_dirty_) {
+ return offset_cache_;
+ }
+
+ std::lock_guard lock{offset_cache_mutex_};
+ if (!offset_cache_dirty_) {
+ return offset_cache_;
+ }
+
+ const int points_len = this->size();
+ offset_cache_.resize(points_len + 1);
+
+ MutableSpan<int> offsets = offset_cache_;
+
+ int offset = 0;
+ for (const int i : IndexRange(points_len)) {
+ offsets[i] = offset;
+ offset += this->segment_is_vector(i) ? 1 : resolution_;
+ }
+ offsets.last() = offset;
+
+ offset_cache_dirty_ = false;
+ return offsets;
+}
+
+static void calculate_mappings_linear_resolution(Span<int> offsets,
+ const int size,
+ const int resolution,
+ const bool is_cyclic,
+ MutableSpan<float> r_mappings)
+{
+ const float first_segment_len_inv = 1.0f / offsets[1];
+ for (const int i : IndexRange(0, offsets[1])) {
+ r_mappings[i] = i * first_segment_len_inv;
+ }
+
+ const int grain_size = std::max(2048 / resolution, 1);
+ parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
+ for (const int i_control_point : range) {
+ const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point];
+ const float segment_len_inv = 1.0f / segment_len;
+ for (const int i : IndexRange(segment_len)) {
+ r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv;
+ }
+ }
+ });
+
+ if (is_cyclic) {
+ const int last_segment_len = offsets[size] - offsets[size - 1];
+ const float last_segment_len_inv = 1.0f / last_segment_len;
+ for (const int i : IndexRange(last_segment_len)) {
+ r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv;
+ }
+ }
+ else {
+ r_mappings.last() = size - 1;
+ }
+}
+
+/**
+ * Returns non-owning access to an array of values containing the information necessary to
+ * interpolate values from the original control points to evaluated points. The control point
+ * index is the integer part of each value, and the factor used for interpolating to the next
+ * control point is the remaining factional part.
+ */
+Span<float> BezierSpline::evaluated_mappings() const
+{
+ if (!mapping_cache_dirty_) {
+ return evaluated_mapping_cache_;
+ }
+
+ std::lock_guard lock{mapping_cache_mutex_};
+ if (!mapping_cache_dirty_) {
+ return evaluated_mapping_cache_;
+ }
+
+ const int size = this->size();
+ const int eval_size = this->evaluated_points_size();
+ evaluated_mapping_cache_.resize(eval_size);
+ MutableSpan<float> mappings = evaluated_mapping_cache_;
+
+ if (eval_size == 1) {
+ mappings.first() = 0.0f;
+ mapping_cache_dirty_ = false;
+ return mappings;
+ }
+
+ Span<int> offsets = this->control_point_offsets();
+
+ calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings);
+
+ mapping_cache_dirty_ = false;
+ return mappings;
+}
+
+Span<float3> BezierSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ this->ensure_auto_handles();
+
+ const int size = this->size();
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ MutableSpan<float3> positions = evaluated_position_cache_;
+
+ Span<int> offsets = this->control_point_offsets();
+
+ const int grain_size = std::max(512 / resolution_, 1);
+ parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) {
+ for (const int i : range) {
+ this->evaluate_bezier_segment(
+ i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
+ }
+ });
+ if (is_cyclic_) {
+ this->evaluate_bezier_segment(
+ size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1]));
+ }
+ else {
+ /* Since evaluating the bezier segment doesn't add the final point,
+ * it must be added manually in the non-cyclic case. */
+ positions.last() = positions_.last();
+ }
+
+ position_cache_dirty_ = false;
+ return positions;
+}
+
+/**
+ * Convert the data encoded in #evaulated_mappings into its parts-- the information necessary
+ * to interpolate data from control points to evaluated points between them. The next control
+ * point index result will not overflow the size of the control point vectors.
+ */
+BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor(
+ const float index_factor) const
+{
+ const int points_len = this->size();
+
+ if (is_cyclic_) {
+ if (index_factor < points_len) {
+ const int index = std::floor(index_factor);
+ const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ return InterpolationData{index, next_index, index_factor - index};
+ }
+ return InterpolationData{points_len - 1, 0, 1.0f};
+ }
+
+ if (index_factor < points_len - 1) {
+ const int index = std::floor(index_factor);
+ const int next_index = index + 1;
+ return InterpolationData{index, next_index, index_factor - index};
+ }
+ return InterpolationData{points_len - 2, points_len - 1, 1.0f};
+}
+
+/* Use a spline argument to avoid adding this to the header. */
+template<typename T>
+static void interpolate_to_evaluated_points_impl(const BezierSpline &spline,
+ const blender::VArray<T> &source_data,
+ MutableSpan<T> result_data)
+{
+ Span<float> mappings = spline.evaluated_mappings();
+
+ for (const int i : result_data.index_range()) {
+ BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor(
+ mappings[i]);
+
+ const T &value = source_data[interp.control_point_index];
+ const T &next_value = source_data[interp.next_control_point_index];
+
+ result_data[i] = blender::attribute_math::mix2(interp.factor, value, next_value);
+ }
+}
+
+blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ if (source_data.is_single()) {
+ return source_data.shallow_copy();
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ if (eval_size == 1) {
+ return source_data.shallow_copy();
+ }
+
+ blender::fn::GVArrayPtr new_varray;
+ blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
+ Array<T> values(eval_size);
+ interpolate_to_evaluated_points_impl<T>(*this, source_data.typed<T>(), values);
+ new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
+ std::move(values));
+ }
+ });
+
+ return new_varray;
+}
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
new file mode 100644
index 00000000000..bfb0d652b1a
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -0,0 +1,444 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+using blender::fn::GVArray_Typed;
+
+SplinePtr NURBSpline::copy() const
+{
+ return std::make_unique<NURBSpline>(*this);
+}
+
+SplinePtr NURBSpline::copy_settings() const
+{
+ std::unique_ptr<NURBSpline> copy = std::make_unique<NURBSpline>();
+ copy_base_settings(*this, *copy);
+ copy->knots_mode = knots_mode;
+ copy->resolution_ = resolution_;
+ copy->order_ = order_;
+ return copy;
+}
+
+int NURBSpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ BLI_assert(size == weights_.size());
+ return size;
+}
+
+int NURBSpline::resolution() const
+{
+ return resolution_;
+}
+
+void NURBSpline::set_resolution(const int value)
+{
+ BLI_assert(value > 0);
+ resolution_ = value;
+ this->mark_cache_invalid();
+}
+
+uint8_t NURBSpline::order() const
+{
+ return order_;
+}
+
+void NURBSpline::set_order(const uint8_t value)
+{
+ BLI_assert(value >= 2 && value <= 6);
+ order_ = value;
+ this->mark_cache_invalid();
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void NURBSpline::add_point(const float3 position,
+ const float radius,
+ const float tilt,
+ const float weight)
+{
+ positions_.append(position);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ weights_.append(weight);
+ knots_dirty_ = true;
+ this->mark_cache_invalid();
+}
+
+void NURBSpline::resize(const int size)
+{
+ positions_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ weights_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> NURBSpline::positions()
+{
+ return positions_;
+}
+Span<float3> NURBSpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> NURBSpline::radii()
+{
+ return radii_;
+}
+Span<float> NURBSpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> NURBSpline::tilts()
+{
+ return tilts_;
+}
+Span<float> NURBSpline::tilts() const
+{
+ return tilts_;
+}
+MutableSpan<float> NURBSpline::weights()
+{
+ return weights_;
+}
+Span<float> NURBSpline::weights() const
+{
+ return weights_;
+}
+
+void NURBSpline::mark_cache_invalid()
+{
+ basis_cache_dirty_ = true;
+ position_cache_dirty_ = true;
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+}
+
+int NURBSpline::evaluated_points_size() const
+{
+ if (!this->check_valid_size_and_order()) {
+ return 0;
+ }
+ return resolution_ * this->segments_size();
+}
+
+void NURBSpline::correct_end_tangents() const
+{
+}
+
+bool NURBSpline::check_valid_size_and_order() const
+{
+ if (this->size() < order_) {
+ return false;
+ }
+
+ if (!is_cyclic_ && this->knots_mode == KnotsMode::Bezier) {
+ if (order_ == 4) {
+ if (this->size() < 5) {
+ return false;
+ }
+ }
+ else if (order_ != 3) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int NURBSpline::knots_size() const
+{
+ const int size = this->size() + order_;
+ return is_cyclic_ ? size + order_ - 1 : size;
+}
+
+void NURBSpline::calculate_knots() const
+{
+ const KnotsMode mode = this->knots_mode;
+ const int length = this->size();
+ const int order = order_;
+
+ knots_.resize(this->knots_size());
+
+ MutableSpan<float> knots = knots_;
+
+ if (mode == NURBSpline::KnotsMode::Normal || is_cyclic_) {
+ for (const int i : knots.index_range()) {
+ knots[i] = static_cast<float>(i);
+ }
+ }
+ else if (mode == NURBSpline::KnotsMode::EndPoint) {
+ float k = 0.0f;
+ for (const int i : IndexRange(1, knots.size())) {
+ knots[i - 1] = k;
+ if (i >= order && i <= length) {
+ k += 1.0f;
+ }
+ }
+ }
+ else if (mode == NURBSpline::KnotsMode::Bezier) {
+ BLI_assert(ELEM(order, 3, 4));
+ if (order == 3) {
+ float k = 0.6f;
+ for (const int i : knots.index_range()) {
+ if (i >= order && i <= length) {
+ k += 0.5f;
+ }
+ knots[i] = std::floor(k);
+ }
+ }
+ else {
+ float k = 0.34f;
+ for (const int i : knots.index_range()) {
+ knots[i] = std::floor(k);
+ k += 1.0f / 3.0f;
+ }
+ }
+ }
+
+ if (is_cyclic_) {
+ const int b = length + order - 1;
+ if (order > 2) {
+ for (const int i : IndexRange(1, order - 2)) {
+ if (knots[b] != knots[b - i]) {
+ if (i == order - 1) {
+ knots[length + order - 2] += 1.0f;
+ break;
+ }
+ }
+ }
+ }
+
+ int c = order;
+ for (int i = b; i < this->knots_size(); i++) {
+ knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
+ c--;
+ }
+ }
+}
+
+Span<float> NURBSpline::knots() const
+{
+ if (!knots_dirty_) {
+ BLI_assert(knots_.size() == this->size() + order_);
+ return knots_;
+ }
+
+ std::lock_guard lock{knots_mutex_};
+ if (!knots_dirty_) {
+ BLI_assert(knots_.size() == this->size() + order_);
+ return knots_;
+ }
+
+ this->calculate_knots();
+
+ knots_dirty_ = false;
+
+ return knots_;
+}
+
+static void calculate_basis_for_point(const float parameter,
+ const int points_len,
+ const int order,
+ Span<float> knots,
+ MutableSpan<float> basis_buffer,
+ NURBSpline::BasisCache &basis_cache)
+{
+ /* Clamp parameter due to floating point inaccuracy. */
+ const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]);
+
+ int start = 0;
+ int end = 0;
+ for (const int i : IndexRange(points_len + order - 1)) {
+ const bool knots_equal = knots[i] == knots[i + 1];
+ if (knots_equal || t < knots[i] || t > knots[i + 1]) {
+ basis_buffer[i] = 0.0f;
+ continue;
+ }
+
+ basis_buffer[i] = 1.0f;
+ start = std::max(i - order - 1, 0);
+ end = i;
+ basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f);
+ break;
+ }
+ basis_buffer[points_len + order - 1] = 0.0f;
+
+ for (const int i_order : IndexRange(2, order - 1)) {
+ if (end + i_order >= points_len + order) {
+ end = points_len + order - 1 - i_order;
+ }
+ for (const int i : IndexRange(start, end - start + 1)) {
+ float new_basis = 0.0f;
+ if (basis_buffer[i] != 0.0f) {
+ new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]);
+ }
+
+ if (basis_buffer[i + 1] != 0.0f) {
+ new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) /
+ (knots[i + i_order] - knots[i + 1]);
+ }
+
+ basis_buffer[i] = new_basis;
+ }
+ }
+
+ /* Shrink the range of calculated values to avoid storing unnecessary zeros. */
+ while (basis_buffer[start] == 0.0f && start < end) {
+ start++;
+ }
+ while (basis_buffer[end] == 0.0f && end > start) {
+ end--;
+ }
+
+ basis_cache.weights.clear();
+ basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1));
+ basis_cache.start_index = start;
+}
+
+void NURBSpline::calculate_basis_cache() const
+{
+ if (!basis_cache_dirty_) {
+ return;
+ }
+
+ std::lock_guard lock{basis_cache_mutex_};
+ if (!basis_cache_dirty_) {
+ return;
+ }
+
+ const int points_len = this->size();
+ const int eval_size = this->evaluated_points_size();
+ BLI_assert(this->evaluated_edges_size() > 0);
+ basis_cache_.resize(eval_size);
+
+ const int order = this->order();
+ Span<float> control_weights = this->weights();
+ Span<float> knots = this->knots();
+
+ MutableSpan<BasisCache> basis_cache(basis_cache_);
+
+ /* This buffer is reused by each basis calculation to store temporary values.
+ * Theoretically it could be optimized away in the future. */
+ Array<float> basis_buffer(this->knots_size());
+
+ const float start = knots[order - 1];
+ const float end = is_cyclic_ ? knots[points_len + order - 1] : knots[points_len];
+ const float step = (end - start) / this->evaluated_edges_size();
+ float parameter = start;
+ for (const int i : IndexRange(eval_size)) {
+ BasisCache &basis = basis_cache[i];
+ calculate_basis_for_point(
+ parameter, points_len + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis);
+ BLI_assert(basis.weights.size() <= order);
+
+ for (const int j : basis.weights.index_range()) {
+ const int point_index = (basis.start_index + j) % points_len;
+ basis.weights[j] *= control_weights[point_index];
+ }
+
+ parameter += step;
+ }
+
+ basis_cache_dirty_ = false;
+}
+
+template<typename T>
+void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights,
+ const blender::VArray<T> &source_data,
+ MutableSpan<T> result_data)
+{
+ const int points_len = source_data.size();
+ BLI_assert(result_data.size() == weights.size());
+ blender::attribute_math::DefaultMixer<T> mixer(result_data);
+
+ for (const int i : result_data.index_range()) {
+ Span<float> point_weights = weights[i].weights;
+ const int start_index = weights[i].start_index;
+
+ for (const int j : point_weights.index_range()) {
+ const int point_index = (start_index + j) % points_len;
+ mixer.mix_in(i, source_data[point_index], point_weights[j]);
+ }
+ }
+
+ mixer.finalize();
+}
+
+blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ if (source_data.is_single()) {
+ return source_data.shallow_copy();
+ }
+
+ this->calculate_basis_cache();
+ Span<BasisCache> weights(basis_cache_);
+
+ blender::fn::GVArrayPtr new_varray;
+ blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
+ Array<T> values(this->evaluated_points_size());
+ interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values);
+ new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
+ std::move(values));
+ }
+ });
+
+ return new_varray;
+}
+
+Span<float3> NURBSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ /* TODO: Avoid copying the evaluated data from the temporary array. */
+ GVArray_Typed<float3> evaluated = Spline::interpolate_to_evaluated_points(positions_.as_span());
+ evaluated->materialize(evaluated_position_cache_);
+
+ position_cache_dirty_ = false;
+ return evaluated_position_cache_;
+}
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
new file mode 100644
index 00000000000..5f8e81d5ad0
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_poly.cc
@@ -0,0 +1,124 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_span.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_spline.hh"
+
+using blender::float3;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr PolySpline::copy() const
+{
+ return std::make_unique<PolySpline>(*this);
+}
+
+SplinePtr PolySpline::copy_settings() const
+{
+ std::unique_ptr<PolySpline> copy = std::make_unique<PolySpline>();
+ copy_base_settings(*this, *copy);
+ return copy;
+}
+
+int PolySpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ return size;
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void PolySpline::add_point(const float3 position, const float radius, const float tilt)
+{
+ positions_.append(position);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ this->mark_cache_invalid();
+}
+
+void PolySpline::resize(const int size)
+{
+ positions_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> PolySpline::positions()
+{
+ return positions_;
+}
+Span<float3> PolySpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> PolySpline::radii()
+{
+ return radii_;
+}
+Span<float> PolySpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> PolySpline::tilts()
+{
+ return tilts_;
+}
+Span<float> PolySpline::tilts() const
+{
+ return tilts_;
+}
+
+void PolySpline::mark_cache_invalid()
+{
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+}
+
+int PolySpline::evaluated_points_size() const
+{
+ return this->size();
+}
+
+void PolySpline::correct_end_tangents() const
+{
+}
+
+Span<float3> PolySpline::evaluated_positions() const
+{
+ return this->positions();
+}
+
+/**
+ * Poly spline interpolation from control points to evaluated points is a special case, since
+ * the result data is the same as the input data. This function returns a GVArray that points to
+ * the original data. Therefore the lifetime of the returned virtual array must not be longer than
+ * the source data.
+ */
+blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ return source_data.shallow_copy();
+}
diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c
index a59f9e0c633..5f732ba91ab 100644
--- a/source/blender/blenkernel/intern/subdiv_ccg.c
+++ b/source/blender/blenkernel/intern/subdiv_ccg.c
@@ -28,12 +28,14 @@
#include "MEM_guardedalloc.h"
+#include "BLI_ghash.h"
#include "BLI_math_bits.h"
#include "BLI_math_vector.h"
#include "BLI_task.h"
#include "BKE_DerivedMesh.h"
#include "BKE_ccg.h"
+#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_eval.h"
@@ -50,6 +52,11 @@ static void subdiv_ccg_average_inner_face_grids(SubdivCCG *subdiv_ccg,
CCGKey *key,
SubdivCCGFace *face);
+void subdiv_ccg_average_faces_boundaries_and_corners(SubdivCCG *subdiv_ccg,
+ CCGKey *key,
+ struct CCGFace **effected_faces,
+ int num_effected_faces);
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -889,11 +896,12 @@ void BKE_subdiv_ccg_update_normals(SubdivCCG *subdiv_ccg,
return;
}
subdiv_ccg_recalc_modified_inner_grid_normals(subdiv_ccg, effected_faces, num_effected_faces);
- /* TODO(sergey): Only average elements which are adjacent to modified
- * faces. */
+
CCGKey key;
BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
- subdiv_ccg_average_all_boundaries_and_corners(subdiv_ccg, &key);
+
+ subdiv_ccg_average_faces_boundaries_and_corners(
+ subdiv_ccg, &key, effected_faces, num_effected_faces);
}
/** \} */
@@ -1032,6 +1040,9 @@ static void subdiv_ccg_average_inner_grids_task(void *__restrict userdata_v,
typedef struct AverageGridsBoundariesData {
SubdivCCG *subdiv_ccg;
CCGKey *key;
+
+ /* Optional lookup table. Maps task index to index in `subdiv_ccg->adjacent_vertices`. */
+ int *adjacent_edge_index_map;
} AverageGridsBoundariesData;
typedef struct AverageGridsBoundariesTLSData {
@@ -1079,10 +1090,14 @@ static void subdiv_ccg_average_grids_boundary(SubdivCCG *subdiv_ccg,
}
static void subdiv_ccg_average_grids_boundaries_task(void *__restrict userdata_v,
- const int adjacent_edge_index,
+ const int n,
const TaskParallelTLS *__restrict tls_v)
{
AverageGridsBoundariesData *data = userdata_v;
+ const int adjacent_edge_index = data->adjacent_edge_index_map ?
+ data->adjacent_edge_index_map[n] :
+ n;
+
AverageGridsBoundariesTLSData *tls = tls_v->userdata_chunk;
SubdivCCG *subdiv_ccg = data->subdiv_ccg;
CCGKey *key = data->key;
@@ -1100,6 +1115,9 @@ static void subdiv_ccg_average_grids_boundaries_free(const void *__restrict UNUS
typedef struct AverageGridsCornerData {
SubdivCCG *subdiv_ccg;
CCGKey *key;
+
+ /* Optional lookup table. Maps task range index to index in subdiv_ccg->adjacent_vertices*/
+ int *adjacent_vert_index_map;
} AverageGridsCornerData;
static void subdiv_ccg_average_grids_corners(SubdivCCG *subdiv_ccg,
@@ -1128,49 +1146,63 @@ static void subdiv_ccg_average_grids_corners(SubdivCCG *subdiv_ccg,
}
static void subdiv_ccg_average_grids_corners_task(void *__restrict userdata_v,
- const int adjacent_vertex_index,
+ const int n,
const TaskParallelTLS *__restrict UNUSED(tls_v))
{
AverageGridsCornerData *data = userdata_v;
+ const int adjacent_vertex_index = data->adjacent_vert_index_map ?
+ data->adjacent_vert_index_map[n] :
+ n;
SubdivCCG *subdiv_ccg = data->subdiv_ccg;
CCGKey *key = data->key;
SubdivCCGAdjacentVertex *adjacent_vertex = &subdiv_ccg->adjacent_vertices[adjacent_vertex_index];
subdiv_ccg_average_grids_corners(subdiv_ccg, key, adjacent_vertex);
}
-static void subdiv_ccg_average_all_boundaries(SubdivCCG *subdiv_ccg, CCGKey *key)
+static void subdiv_ccg_average_boundaries(SubdivCCG *subdiv_ccg,
+ CCGKey *key,
+ int *adjacent_edge_index_map,
+ int num_adjacent_edges)
{
TaskParallelSettings parallel_range_settings;
BLI_parallel_range_settings_defaults(&parallel_range_settings);
AverageGridsBoundariesData boundaries_data = {
- .subdiv_ccg = subdiv_ccg,
- .key = key,
- };
+ .subdiv_ccg = subdiv_ccg, .key = key, .adjacent_edge_index_map = adjacent_edge_index_map};
AverageGridsBoundariesTLSData tls_data = {NULL};
parallel_range_settings.userdata_chunk = &tls_data;
parallel_range_settings.userdata_chunk_size = sizeof(tls_data);
parallel_range_settings.func_free = subdiv_ccg_average_grids_boundaries_free;
BLI_task_parallel_range(0,
- subdiv_ccg->num_adjacent_edges,
+ num_adjacent_edges,
&boundaries_data,
subdiv_ccg_average_grids_boundaries_task,
&parallel_range_settings);
}
-static void subdiv_ccg_average_all_corners(SubdivCCG *subdiv_ccg, CCGKey *key)
+static void subdiv_ccg_average_all_boundaries(SubdivCCG *subdiv_ccg, CCGKey *key)
+{
+ subdiv_ccg_average_boundaries(subdiv_ccg, key, NULL, subdiv_ccg->num_adjacent_edges);
+}
+
+static void subdiv_ccg_average_corners(SubdivCCG *subdiv_ccg,
+ CCGKey *key,
+ int *adjacent_vert_index_map,
+ int num_adjacent_vertices)
{
TaskParallelSettings parallel_range_settings;
BLI_parallel_range_settings_defaults(&parallel_range_settings);
AverageGridsCornerData corner_data = {
- .subdiv_ccg = subdiv_ccg,
- .key = key,
- };
+ .subdiv_ccg = subdiv_ccg, .key = key, .adjacent_vert_index_map = adjacent_vert_index_map};
BLI_task_parallel_range(0,
- subdiv_ccg->num_adjacent_vertices,
+ num_adjacent_vertices,
&corner_data,
subdiv_ccg_average_grids_corners_task,
&parallel_range_settings);
}
+static void subdiv_ccg_average_all_corners(SubdivCCG *subdiv_ccg, CCGKey *key)
+{
+ subdiv_ccg_average_corners(subdiv_ccg, key, NULL, subdiv_ccg->num_adjacent_vertices);
+}
static void subdiv_ccg_average_all_boundaries_and_corners(SubdivCCG *subdiv_ccg, CCGKey *key)
{
@@ -1198,6 +1230,98 @@ void BKE_subdiv_ccg_average_grids(SubdivCCG *subdiv_ccg)
subdiv_ccg_average_all_boundaries_and_corners(subdiv_ccg, &key);
}
+static void subdiv_ccg_affected_face_adjacency(SubdivCCG *subdiv_ccg,
+ struct CCGFace **effected_faces,
+ int num_effected_faces,
+ GSet *r_adjacent_vertices,
+ GSet *r_adjacent_edges)
+{
+ Subdiv *subdiv = subdiv_ccg->subdiv;
+ OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
+
+ StaticOrHeapIntStorage face_vertices_storage;
+ StaticOrHeapIntStorage face_edges_storage;
+
+ static_or_heap_storage_init(&face_vertices_storage);
+ static_or_heap_storage_init(&face_edges_storage);
+
+ for (int i = 0; i < num_effected_faces; i++) {
+ SubdivCCGFace *face = (SubdivCCGFace *)effected_faces[i];
+ int face_index = face - subdiv_ccg->faces;
+ const int num_face_grids = face->num_grids;
+ const int num_face_edges = num_face_grids;
+ int *face_vertices = static_or_heap_storage_get(&face_vertices_storage, num_face_edges);
+ topology_refiner->getFaceVertices(topology_refiner, face_index, face_vertices);
+
+ /* Note that order of edges is same as order of MLoops, which also
+ * means it's the same as order of grids. */
+ int *face_edges = static_or_heap_storage_get(&face_edges_storage, num_face_edges);
+ topology_refiner->getFaceEdges(topology_refiner, face_index, face_edges);
+ for (int corner = 0; corner < num_face_edges; corner++) {
+ const int vertex_index = face_vertices[corner];
+ const int edge_index = face_edges[corner];
+
+ int edge_vertices[2];
+ topology_refiner->getEdgeVertices(topology_refiner, edge_index, edge_vertices);
+
+ SubdivCCGAdjacentEdge *adjacent_edge = &subdiv_ccg->adjacent_edges[edge_index];
+ BLI_gset_add(r_adjacent_edges, adjacent_edge);
+
+ SubdivCCGAdjacentVertex *adjacent_vertex = &subdiv_ccg->adjacent_vertices[vertex_index];
+ BLI_gset_add(r_adjacent_vertices, adjacent_vertex);
+ }
+ }
+
+ static_or_heap_storage_free(&face_vertices_storage);
+ static_or_heap_storage_free(&face_edges_storage);
+}
+
+void subdiv_ccg_average_faces_boundaries_and_corners(SubdivCCG *subdiv_ccg,
+ CCGKey *key,
+ struct CCGFace **effected_faces,
+ int num_effected_faces)
+{
+ GSet *adjacent_vertices = BLI_gset_ptr_new(__func__);
+ GSet *adjacent_edges = BLI_gset_ptr_new(__func__);
+ GSetIterator gi;
+
+ subdiv_ccg_affected_face_adjacency(
+ subdiv_ccg, effected_faces, num_effected_faces, adjacent_vertices, adjacent_edges);
+
+ int *adjacent_vertex_index_map;
+ int *adjacent_edge_index_map;
+
+ StaticOrHeapIntStorage index_heap;
+ static_or_heap_storage_init(&index_heap);
+
+ int i = 0;
+
+ /* Average boundaries. */
+
+ adjacent_edge_index_map = static_or_heap_storage_get(&index_heap, BLI_gset_len(adjacent_edges));
+ GSET_ITER_INDEX (gi, adjacent_edges, i) {
+ SubdivCCGAdjacentEdge *adjacent_edge = BLI_gsetIterator_getKey(&gi);
+ adjacent_edge_index_map[i] = adjacent_edge - subdiv_ccg->adjacent_edges;
+ }
+ subdiv_ccg_average_boundaries(
+ subdiv_ccg, key, adjacent_edge_index_map, BLI_gset_len(adjacent_edges));
+
+ /* Average corners. */
+
+ adjacent_vertex_index_map = static_or_heap_storage_get(&index_heap,
+ BLI_gset_len(adjacent_vertices));
+ GSET_ITER_INDEX (gi, adjacent_vertices, i) {
+ SubdivCCGAdjacentVertex *adjacent_vertex = BLI_gsetIterator_getKey(&gi);
+ adjacent_vertex_index_map[i] = adjacent_vertex - subdiv_ccg->adjacent_vertices;
+ }
+ subdiv_ccg_average_corners(
+ subdiv_ccg, key, adjacent_vertex_index_map, BLI_gset_len(adjacent_vertices));
+
+ BLI_gset_free(adjacent_vertices, NULL);
+ BLI_gset_free(adjacent_edges, NULL);
+ static_or_heap_storage_free(&index_heap);
+}
+
typedef struct StitchFacesInnerGridsData {
SubdivCCG *subdiv_ccg;
CCGKey *key;
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index a28136f8527..23eccbfba9b 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -1879,12 +1879,11 @@ static const MeshElemMap *ccgDM_getPolyMap(Object *ob, DerivedMesh *dm)
/* WARNING! *MUST* be called in an 'loops_cache_rwlock' protected thread context! */
static void ccgDM_recalcLoopTri(DerivedMesh *dm)
{
- MLoopTri *mlooptri = dm->looptris.array;
const int tottri = dm->numPolyData * 2;
int i, poly_index;
DM_ensure_looptri_data(dm);
- mlooptri = dm->looptris.array_wip;
+ MLoopTri *mlooptri = dm->looptris.array_wip;
BLI_assert(tottri == 0 || mlooptri != NULL);
BLI_assert(poly_to_tri_count(dm->numPolyData, dm->numLoopData) == dm->looptris.num);
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 43d587861a5..27f5593c2ca 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -171,6 +171,9 @@ static void text_free_data(ID *id)
static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
+ if (id->us < 1 && !BLO_write_is_undo(writer)) {
+ return;
+ }
Text *text = (Text *)id;
/* Note: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */
@@ -231,8 +234,6 @@ static void text_blend_read_data(BlendDataReader *reader, ID *id)
}
text->flags = (text->flags) & ~TXT_ISEXT;
-
- id_us_ensure_real(&text->id);
}
IDTypeInfo IDType_ID_TXT = {
@@ -293,8 +294,10 @@ Text *BKE_text_add(Main *bmain, const char *name)
Text *ta;
ta = BKE_id_new(bmain, ID_TXT, name);
- /* Texts always have 'real' user (see also read code). */
- id_us_ensure_real(&ta->id);
+ /* Texts have no users by default... Set the fake user flag to ensure that this text block
+ * doesn't get deleted by default when cleaning up data blocks. */
+ id_us_min(&ta->id);
+ id_fake_user_set(&ta->id);
return ta;
}
@@ -468,7 +471,7 @@ bool BKE_text_reload(Text *text)
* \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 real user but have 'fake user' enabled by default
*/
Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const bool is_internal)
{
@@ -489,9 +492,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
}
ta = BKE_libblock_alloc(bmain, ID_TXT, BLI_path_basename(filepath_abs), 0);
- /* Texts have no user by default... Only the 'real' user flag. */
- id_us_ensure_real(&ta->id);
id_us_min(&ta->id);
+ id_fake_user_set(&ta->id);
BLI_listbase_clear(&ta->lines);
ta->curl = ta->sell = NULL;
@@ -523,7 +525,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
return ta;
}
-/** Load a text file.
+/**
+ * Load a text file.
*
* \note Text data-blocks have no user by default, only the 'real user' flag.
*/
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
index d124922acd1..f3d6bc4a6e3 100644
--- a/source/blender/blenkernel/intern/tracking.c
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -3251,6 +3251,11 @@ static void tracking_dopesheet_calc_coverage(MovieTracking *tracking)
end_frame = max_ii(end_frame, track->markers[track->markersnr - 1].framenr);
}
+ if (start_frame > end_frame) {
+ /* There are no markers at all, nothing to calculate coverage from. */
+ return;
+ }
+
frames = end_frame - start_frame + 1;
/* this is a per-frame counter of markers (how many markers belongs to the same frame) */
diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c
index 9ae1c754846..5cf76bb6452 100644
--- a/source/blender/blenkernel/intern/unit.c
+++ b/source/blender/blenkernel/intern/unit.c
@@ -358,6 +358,7 @@ static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = {
NULL,
&buNaturalRotCollection,
&buNaturalTimeCollection,
+ &buNaturalTimeCollection,
NULL,
NULL,
NULL,
@@ -371,6 +372,7 @@ static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = {
&buMetricMassCollection,
&buNaturalRotCollection,
&buNaturalTimeCollection,
+ &buNaturalTimeCollection,
&buMetricVelCollection,
&buMetricAclCollection,
&buCameraLenCollection,
@@ -384,12 +386,13 @@ static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = {
&buImperialMassCollection,
&buNaturalRotCollection,
&buNaturalTimeCollection,
+ &buNaturalTimeCollection,
&buImperialVelCollection,
&buImperialAclCollection,
&buCameraLenCollection,
&buPowerCollection,
&buImperialTempCollection},
- {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
};
static const bUnitCollection *unit_get_system(int system, int type)
@@ -943,7 +946,7 @@ static int unit_scale_str(char *str,
/* Add the addition sign, the bias, and the close parenthesis after the value. */
int value_end_ofs = find_end_of_value_chars(str, len_max, prev_op_ofs + 2);
- int len_bias_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias);
+ int len_bias_num = BLI_snprintf_rlen(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias);
if (value_end_ofs + len_bias_num < len_max) {
memmove(str + value_end_ofs + len_bias_num, str + value_end_ofs, len - value_end_ofs + 1);
memcpy(str + value_end_ofs, str_tmp, len_bias_num);
@@ -957,7 +960,8 @@ static int unit_scale_str(char *str,
int len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */
/* "#" Removed later */
- int len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref);
+ int len_num = BLI_snprintf_rlen(
+ str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref);
if (len_num > len_max) {
len_num = len_max;
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 9b5231f7d6f..c0ce57818d1 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -28,7 +28,10 @@
#include "BLI_compiler_compat.h"
#include "BLI_fileops.h"
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
#include "BLI_ghash.h"
+#include "BLI_index_range.hh"
#include "BLI_map.hh"
#include "BLI_math.h"
#include "BLI_path_util.h"
@@ -36,6 +39,7 @@
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
+#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -63,6 +67,10 @@ static CLG_LogRef LOG = {"bke.volume"};
#define VOLUME_FRAME_NONE INT_MAX
+using blender::float3;
+using blender::float4x4;
+using blender::IndexRange;
+
#ifdef WITH_OPENVDB
# include <atomic>
# include <list>
@@ -144,14 +152,14 @@ static struct VolumeFileCache {
blender::Map<int, openvdb::GridBase::Ptr> simplified_grids;
/* Has the grid tree been loaded? */
- bool is_loaded;
+ mutable bool is_loaded;
/* Error message if an error occurred while loading. */
std::string error_msg;
/* User counting. */
int num_metadata_users;
int num_tree_users;
/* Mutex for on-demand reading of tree. */
- std::mutex mutex;
+ mutable std::mutex mutex;
};
struct EntryHasher {
@@ -171,10 +179,6 @@ static struct VolumeFileCache {
};
/* Cache */
- VolumeFileCache()
- {
- }
-
~VolumeFileCache()
{
BLI_assert(cache.empty());
@@ -293,7 +297,7 @@ struct VolumeGrid {
}
}
- void load(const char *volume_name, const char *filepath)
+ void load(const char *volume_name, const char *filepath) const
{
/* If already loaded or not file-backed, nothing to do. */
if (is_loaded || entry == nullptr) {
@@ -335,7 +339,7 @@ struct VolumeGrid {
is_loaded = true;
}
- void unload(const char *volume_name)
+ void unload(const char *volume_name) const
{
/* Not loaded or not file-backed, nothing to do. */
if (!is_loaded || entry == nullptr) {
@@ -436,10 +440,14 @@ struct VolumeGrid {
int simplify_level = 0;
/* OpenVDB grid if it's not shared through the file cache. */
openvdb::GridBase::Ptr local_grid;
- /* Indicates if the tree has been loaded for this grid. Note that vdb.tree()
+ /**
+ * Indicates if the tree has been loaded for this grid. Note that vdb.tree()
* may actually be loaded by another user while this is false. But only after
- * calling load() and is_loaded changes to true is it safe to access. */
- bool is_loaded;
+ * calling load() and is_loaded changes to true is it safe to access.
+ *
+ * Const write access to this must be protected by `entry->mutex`.
+ */
+ mutable bool is_loaded;
};
/* Volume Grid Vector
@@ -472,14 +480,15 @@ struct VolumeGridVector : public std::list<VolumeGrid> {
metadata.reset();
}
+ /* Mutex for file loading of grids list. Const write access to the fields after this must be
+ * protected by locking with this mutex. */
+ mutable std::mutex mutex;
/* Absolute file path that grids have been loaded from. */
char filepath[FILE_MAX];
/* File loading error message. */
std::string error_msg;
/* File Metadata. */
openvdb::MetaMap::Ptr metadata;
- /* Mutex for file loading of grids list. */
- std::mutex mutex;
};
#endif
@@ -766,10 +775,10 @@ bool BKE_volume_is_loaded(const Volume *volume)
#endif
}
-bool BKE_volume_load(Volume *volume, Main *bmain)
+bool BKE_volume_load(const Volume *volume, const Main *bmain)
{
#ifdef WITH_OPENVDB
- VolumeGridVector &grids = *volume->runtime.grids;
+ const VolumeGridVector &const_grids = *volume->runtime.grids;
if (volume->runtime.frame == VOLUME_FRAME_NONE) {
/* Skip loading this frame, outside of sequence range. */
@@ -777,15 +786,19 @@ bool BKE_volume_load(Volume *volume, Main *bmain)
}
if (BKE_volume_is_loaded(volume)) {
- return grids.error_msg.empty();
+ return const_grids.error_msg.empty();
}
/* Double-checked lock. */
- std::lock_guard<std::mutex> lock(grids.mutex);
+ std::lock_guard<std::mutex> lock(const_grids.mutex);
if (BKE_volume_is_loaded(volume)) {
- return grids.error_msg.empty();
+ return const_grids.error_msg.empty();
}
+ /* Guarded by the lock, we can continue to access the grid vector,
+ * adding error messages or a new grid, etc. */
+ VolumeGridVector &grids = const_cast<VolumeGridVector &>(const_grids);
+
/* Get absolute file path at current frame. */
const char *volume_name = volume->id.name + 2;
char filepath[FILE_MAX];
@@ -850,7 +863,10 @@ void BKE_volume_unload(Volume *volume)
/* File Save */
-bool BKE_volume_save(Volume *volume, Main *bmain, ReportList *reports, const char *filepath)
+bool BKE_volume_save(const Volume *volume,
+ const Main *bmain,
+ ReportList *reports,
+ const char *filepath)
{
#ifdef WITH_OPENVDB
if (!BKE_volume_load(volume, bmain)) {
@@ -882,6 +898,32 @@ bool BKE_volume_save(Volume *volume, Main *bmain, ReportList *reports, const cha
#endif
}
+bool BKE_volume_min_max(const Volume *volume, float3 &r_min, float3 &r_max)
+{
+ bool have_minmax = false;
+#ifdef WITH_OPENVDB
+ /* TODO: if we know the volume is going to be displayed, it may be good to
+ * load it as part of dependency graph evaluation for better threading. We
+ * could also share the bounding box computation in the global volume cache. */
+ if (BKE_volume_load(const_cast<Volume *>(volume), G.main)) {
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+ float3 grid_min;
+ float3 grid_max;
+ if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) {
+ DO_MIN(grid_min, r_min);
+ DO_MAX(grid_max, r_max);
+ have_minmax = true;
+ }
+ }
+ }
+#else
+ UNUSED_VARS(volume, r_min, r_max);
+#endif
+ return have_minmax;
+}
+
BoundBox *BKE_volume_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_VOLUME);
@@ -891,41 +933,20 @@ BoundBox *BKE_volume_boundbox_get(Object *ob)
}
if (ob->runtime.bb == nullptr) {
- Volume *volume = (Volume *)ob->data;
-
- ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "volume boundbox");
-
- float min[3], max[3];
- bool have_minmax = false;
- INIT_MINMAX(min, max);
-
- /* TODO: if we know the volume is going to be displayed, it may be good to
- * load it as part of dependency graph evaluation for better threading. We
- * could also share the bounding box computation in the global volume cache. */
- if (BKE_volume_load(volume, G.main)) {
- const int num_grids = BKE_volume_num_grids(volume);
-
- for (int i = 0; i < num_grids; i++) {
- VolumeGrid *grid = BKE_volume_grid_get(volume, i);
- float grid_min[3], grid_max[3];
-
- BKE_volume_grid_load(volume, grid);
- if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) {
- DO_MIN(grid_min, min);
- DO_MAX(grid_max, max);
- have_minmax = true;
- }
- }
- }
+ ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), __func__);
+ }
- if (!have_minmax) {
- min[0] = min[1] = min[2] = -1.0f;
- max[0] = max[1] = max[2] = 1.0f;
- }
+ const Volume *volume = (Volume *)ob->data;
- BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ float3 min, max;
+ INIT_MINMAX(min, max);
+ if (!BKE_volume_min_max(volume, min, max)) {
+ min = float3(-1);
+ max = float3(1);
}
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+
return ob->runtime.bb;
}
@@ -957,7 +978,7 @@ bool BKE_volume_is_points_only(const Volume *volume)
}
for (int i = 0; i < num_grids; i++) {
- VolumeGrid *grid = BKE_volume_grid_get(volume, i);
+ const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
if (BKE_volume_grid_type(grid) != VOLUME_GRID_POINTS) {
return false;
}
@@ -983,13 +1004,11 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr
#endif
}
-static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
- struct Scene *scene,
- Object *object,
- Volume *volume_input)
+static void volume_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
+ GeometrySet &geometry_set)
{
- Volume *volume = volume_input;
-
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -1010,25 +1029,10 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
- if (mti->modifyVolume) {
- /* Ensure we are not modifying the input. */
- if (volume == volume_input) {
- volume = BKE_volume_copy_for_eval(volume, true);
- }
-
- Volume *volume_next = mti->modifyVolume(md, &mectx, volume);
-
- if (volume_next && volume_next != volume) {
- /* If the modifier returned a new volume, release the old one. */
- if (volume != volume_input) {
- BKE_id_free(nullptr, volume);
- }
- volume = volume_next;
- }
+ if (mti->modifyGeometrySet) {
+ mti->modifyGeometrySet(md, &mectx, &geometry_set);
}
}
-
- return volume;
}
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
@@ -1052,6 +1056,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
}
}
+static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set)
+{
+ if (!geometry_set.has<VolumeComponent>()) {
+ return nullptr;
+ }
+ VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
+ Volume *volume = volume_component.release();
+ if (volume != nullptr) {
+ /* Add back, but only as read-only non-owning component. */
+ volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
+ }
+ else {
+ /* The component was empty, we can remove it. */
+ geometry_set.remove<VolumeComponent>();
+ }
+ return volume;
+}
+
void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
{
/* Free any evaluated data and restore original data. */
@@ -1059,11 +1081,21 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
/* Evaluate modifiers. */
Volume *volume = (Volume *)object->data;
- Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume);
+ GeometrySet geometry_set;
+ geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly);
+ volume_evaluate_modifiers(depsgraph, scene, object, geometry_set);
+
+ Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set);
+
+ /* If the geometry set did not contain a volume, we still create an empty one. */
+ if (volume_eval == nullptr) {
+ volume_eval = BKE_volume_new_for_eval(volume);
+ }
/* Assign evaluated object. */
- const bool is_owned = (volume != volume_eval);
- BKE_object_eval_assign_data(object, &volume_eval->id, is_owned);
+ const bool eval_is_owned = (volume != volume_eval);
+ BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned);
+ object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)
@@ -1144,7 +1176,23 @@ const char *BKE_volume_grids_frame_filepath(const Volume *volume)
#endif
}
-VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index)
+const VolumeGrid *BKE_volume_grid_get_for_read(const Volume *volume, int grid_index)
+{
+#ifdef WITH_OPENVDB
+ const VolumeGridVector &grids = *volume->runtime.grids;
+ for (const VolumeGrid &grid : grids) {
+ if (grid_index-- == 0) {
+ return &grid;
+ }
+ }
+ return nullptr;
+#else
+ UNUSED_VARS(volume, grid_index);
+ return nullptr;
+#endif
+}
+
+VolumeGrid *BKE_volume_grid_get_for_write(Volume *volume, int grid_index)
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
@@ -1160,7 +1208,7 @@ VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index)
#endif
}
-VolumeGrid *BKE_volume_grid_active_get(const Volume *volume)
+const VolumeGrid *BKE_volume_grid_active_get_for_read(const Volume *volume)
{
const int num_grids = BKE_volume_num_grids(volume);
if (num_grids == 0) {
@@ -1168,15 +1216,15 @@ VolumeGrid *BKE_volume_grid_active_get(const Volume *volume)
}
const int index = clamp_i(volume->active_grid, 0, num_grids - 1);
- return BKE_volume_grid_get(volume, index);
+ return BKE_volume_grid_get_for_read(volume, index);
}
/* Tries to find a grid with the given name. Make sure that that the volume has been loaded. */
-VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name)
+const VolumeGrid *BKE_volume_grid_find_for_read(const Volume *volume, const char *name)
{
int num_grids = BKE_volume_num_grids(volume);
for (int i = 0; i < num_grids; i++) {
- VolumeGrid *grid = BKE_volume_grid_get(volume, i);
+ const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
if (STREQ(BKE_volume_grid_name(grid), name)) {
return grid;
}
@@ -1187,7 +1235,7 @@ VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name)
/* Grid Loading */
-bool BKE_volume_grid_load(const Volume *volume, VolumeGrid *grid)
+bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid)
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
@@ -1205,7 +1253,7 @@ bool BKE_volume_grid_load(const Volume *volume, VolumeGrid *grid)
#endif
}
-void BKE_volume_grid_unload(const Volume *volume, VolumeGrid *grid)
+void BKE_volume_grid_unload(const Volume *volume, const VolumeGrid *grid)
{
#ifdef WITH_OPENVDB
const char *volume_name = volume->id.name + 2;
@@ -1335,34 +1383,6 @@ void BKE_volume_grid_transform_matrix(const VolumeGrid *volume_grid, float mat[4
/* Grid Tree and Voxels */
-bool BKE_volume_grid_bounds(const VolumeGrid *volume_grid, float min[3], float max[3])
-{
-#ifdef WITH_OPENVDB
- /* TODO: we can get this from grid metadata in some cases? */
- const openvdb::GridBase::Ptr grid = volume_grid->grid();
- BLI_assert(BKE_volume_grid_is_loaded(volume_grid));
-
- openvdb::CoordBBox coordbbox;
- if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) {
- INIT_MINMAX(min, max);
- return false;
- }
-
- openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox);
- min[0] = (float)bbox.min().x();
- min[1] = (float)bbox.min().y();
- min[2] = (float)bbox.min().z();
- max[0] = (float)bbox.max().x();
- max[1] = (float)bbox.max().y();
- max[2] = (float)bbox.max().z();
- return true;
-#else
- UNUSED_VARS(volume_grid);
- INIT_MINMAX(min, max);
- return false;
-#endif
-}
-
/* Volume Editing */
Volume *BKE_volume_new_for_eval(const Volume *volume_src)
@@ -1410,7 +1430,7 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
- BLI_assert(BKE_volume_grid_find(volume, name) == nullptr);
+ BLI_assert(BKE_volume_grid_find_for_read(volume, name) == nullptr);
BLI_assert(type != VOLUME_GRID_UNKNOWN);
openvdb::GridBase::Ptr vdb_grid = BKE_volume_grid_type_operation(type, CreateGridOp{});
@@ -1472,13 +1492,45 @@ float BKE_volume_simplify_factor(const Depsgraph *depsgraph)
/* OpenVDB Grid Access */
#ifdef WITH_OPENVDB
+
+bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, float3 &r_min, float3 &r_max)
+{
+ /* TODO: we can get this from grid metadata in some cases? */
+ openvdb::CoordBBox coordbbox;
+ if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) {
+ return false;
+ }
+
+ openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox);
+
+ r_min = float3((float)bbox.min().x(), (float)bbox.min().y(), (float)bbox.min().z());
+ r_max = float3((float)bbox.max().x(), (float)bbox.max().y(), (float)bbox.max().z());
+
+ return true;
+}
+
+/**
+ * Return a new grid pointer with only the metadata and transform changed.
+ * This is useful for instances, where there is a separate transform on top of the original
+ * grid transform that must be applied for some operations that only take a grid argument.
+ */
+openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid,
+ const blender::float4x4 &transform)
+{
+ openvdb::math::Transform::Ptr grid_transform = grid->transform().copy();
+ grid_transform->postMult(openvdb::Mat4d(((float *)transform.values)));
+
+ /* Create a transformed grid. The underlying tree is shared. */
+ return grid->copyGridReplacingTransform(grid_transform);
+}
+
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const VolumeGrid *grid)
{
return grid->grid();
}
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const Volume *volume,
- VolumeGrid *grid)
+ const VolumeGrid *grid)
{
BKE_volume_grid_load(volume, grid);
return grid->grid();
diff --git a/source/blender/blenkernel/intern/volume_render.cc b/source/blender/blenkernel/intern/volume_render.cc
index 5c71f1d7eca..6dc497bb616 100644
--- a/source/blender/blenkernel/intern/volume_render.cc
+++ b/source/blender/blenkernel/intern/volume_render.cc
@@ -104,7 +104,7 @@ static void create_texture_to_object_matrix(const openvdb::Mat4d &grid_transform
#endif
bool BKE_volume_grid_dense_floats(const Volume *volume,
- VolumeGrid *volume_grid,
+ const VolumeGrid *volume_grid,
DenseFloatVolumeGrid *r_dense_grid)
{
#ifdef WITH_OPENVDB
@@ -334,7 +334,7 @@ static void boxes_to_cube_mesh(blender::Span<openvdb::CoordBBox> boxes,
#endif
void BKE_volume_grid_wireframe(const Volume *volume,
- VolumeGrid *volume_grid,
+ const VolumeGrid *volume_grid,
BKE_volume_wireframe_cb cb,
void *cb_userdata)
{
@@ -411,7 +411,7 @@ static void grow_triangles(blender::MutableSpan<blender::float3> verts,
#endif /* WITH_OPENVDB */
void BKE_volume_grid_selection_surface(const Volume *volume,
- VolumeGrid *volume_grid,
+ const VolumeGrid *volume_grid,
BKE_volume_selection_surface_cb cb,
void *cb_userdata)
{
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 0991d804882..560ae30967f 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -36,6 +36,7 @@
# include <AUD_Special.h>
# endif
+# include "BLI_endian_defines.h"
# include "BLI_math_base.h"
# include "BLI_threads.h"
# include "BLI_utildefines.h"
@@ -56,6 +57,7 @@
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libavutil/imgutils.h>
+# include <libavutil/opt.h>
# include <libavutil/rational.h>
# include <libavutil/samplefmt.h>
# include <libswscale/swscale.h>
@@ -80,6 +82,8 @@ typedef struct FFMpegContext {
int ffmpeg_preset; /* see eFFMpegPreset */
AVFormatContext *outfile;
+ AVCodecContext *video_codec;
+ AVCodecContext *audio_codec;
AVStream *video_stream;
AVStream *audio_stream;
AVFrame *current_frame; /* Image frame in output pixel format. */
@@ -91,10 +95,6 @@ typedef struct FFMpegContext {
uint8_t *audio_input_buffer;
uint8_t *audio_deinterleave_buffer;
int audio_input_samples;
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- uint8_t *audio_output_buffer;
- int audio_outbuf_size;
-# endif
double audio_time;
bool audio_deinterleave;
int audio_sample_size;
@@ -141,32 +141,22 @@ static int request_float_audio_buffer(int codec_id)
}
# ifdef WITH_AUDASPACE
+
static int write_audio_frame(FFMpegContext *context)
{
- AVCodecContext *c = NULL;
- AVPacket pkt;
AVFrame *frame = NULL;
- int got_output = 0;
-
- c = context->audio_stream->codec;
-
- av_init_packet(&pkt);
- pkt.size = 0;
- pkt.data = NULL;
+ AVCodecContext *c = context->audio_codec;
AUD_Device_read(
context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples);
context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate;
-# ifdef FFMPEG_HAVE_ENCODE_AUDIO2
frame = av_frame_alloc();
- av_frame_unref(frame);
frame->pts = context->audio_time / av_q2d(c->time_base);
frame->nb_samples = context->audio_input_samples;
frame->format = c->sample_fmt;
-# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
+ frame->channels = c->channels;
frame->channel_layout = c->channel_layout;
-# endif
if (context->audio_deinterleave) {
int channel, i;
@@ -194,61 +184,48 @@ static int write_audio_frame(FFMpegContext *context)
context->audio_input_samples * c->channels * context->audio_sample_size,
1);
- if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) {
- // XXX error("Error writing audio packet");
- return -1;
- }
+ int success = 0;
- if (!got_output) {
- av_frame_free(&frame);
- return 0;
+ int ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send audio frame: %s\n", av_err2str(ret));
+ success = -1;
}
-# else
- pkt.size = avcodec_encode_audio(c,
- context->audio_output_buffer,
- context->audio_outbuf_size,
- (short *)context->audio_input_buffer);
- if (pkt.size < 0) {
- // XXX error("Error writing audio packet");
- return -1;
- }
+ AVPacket *pkt = av_packet_alloc();
- pkt.data = context->audio_output_buffer;
- got_output = 1;
-# endif
+ while (ret >= 0) {
- if (got_output) {
- if (pkt.pts != AV_NOPTS_VALUE) {
- pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base);
- }
- if (pkt.dts != AV_NOPTS_VALUE) {
- pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base);
+ ret = avcodec_receive_packet(c, pkt);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
}
- if (pkt.duration > 0) {
- pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base);
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
+ success = -1;
}
- pkt.stream_index = context->audio_stream->index;
+ pkt->stream_index = context->audio_stream->index;
+ av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(context->outfile, context->audio_stream, pkt);
+# endif
- pkt.flags |= AV_PKT_FLAG_KEY;
+ pkt->flags |= AV_PKT_FLAG_KEY;
- if (av_interleaved_write_frame(context->outfile, &pkt) != 0) {
- fprintf(stderr, "Error writing audio packet!\n");
- if (frame) {
- av_frame_free(&frame);
- }
- return -1;
+ int write_ret = av_interleaved_write_frame(context->outfile, pkt);
+ if (write_ret != 0) {
+ fprintf(stderr, "Error writing audio packet: %s\n", av_err2str(write_ret));
+ success = -1;
+ break;
}
-
- av_free_packet(&pkt);
}
- if (frame) {
- av_frame_free(&frame);
- }
+ av_packet_free(&pkt);
+ av_frame_free(&frame);
- return 0;
+ return success;
}
# endif /* #ifdef WITH_AUDASPACE */
@@ -264,14 +241,15 @@ static AVFrame *alloc_picture(int pix_fmt, int width, int height)
if (!f) {
return NULL;
}
- size = avpicture_get_size(pix_fmt, width, height);
+ size = av_image_get_buffer_size(pix_fmt, width, height, 1);
/* allocate the actual picture buffer */
buf = MEM_mallocN(size, "AVFrame buffer");
if (!buf) {
free(f);
return NULL;
}
- avpicture_fill((AVPicture *)f, buf, pix_fmt, width, height);
+
+ av_image_fill_arrays(f->data, f->linesize, buf, pix_fmt, width, height, 1);
f->format = pix_fmt;
f->width = width;
f->height = height;
@@ -341,58 +319,61 @@ static const char **get_file_extensions(int format)
}
/* Write a frame to the output file */
-static int write_video_frame(
- FFMpegContext *context, const RenderData *rd, int cfra, AVFrame *frame, ReportList *reports)
+static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, ReportList *reports)
{
- int got_output;
int ret, success = 1;
- AVCodecContext *c = context->video_stream->codec;
- AVPacket packet = {0};
+ AVPacket *packet = av_packet_alloc();
- av_init_packet(&packet);
+ AVCodecContext *c = context->video_codec;
frame->pts = cfra;
- ret = avcodec_encode_video2(c, &packet, frame, &got_output);
+ ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret));
+ success = -1;
+ }
- if (ret >= 0 && got_output) {
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame PTS: %d\n", (int)packet.pts);
- }
- else {
- PRINT("Video Frame PTS: not set\n");
- }
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame DTS: %d\n", (int)packet.dts);
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, packet);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets available. */
+ break;
}
- else {
- PRINT("Video Frame DTS: not set\n");
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding frame: %s\n", av_err2str(ret));
+ break;
}
- packet.stream_index = context->video_stream->index;
- ret = av_interleaved_write_frame(context->outfile, &packet);
- success = (ret == 0);
- }
- else if (ret < 0) {
- success = 0;
+ packet->stream_index = context->video_stream->index;
+ av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(context->outfile, context->video_stream, packet);
+# endif
+
+ if (av_interleaved_write_frame(context->outfile, packet) != 0) {
+ success = -1;
+ break;
+ }
}
if (!success) {
BKE_report(reports, RPT_ERROR, "Error writing frame");
+ PRINT("Error writing frame: %s\n", av_err2str(ret));
}
+ av_packet_free(&packet);
+
return success;
}
/* read and encode a frame of audio from the buffer */
-static AVFrame *generate_video_frame(FFMpegContext *context,
- const uint8_t *pixels,
- ReportList *reports)
+static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels)
{
- AVCodecContext *c = context->video_stream->codec;
- int height = c->height;
+ AVCodecParameters *codec = context->video_stream->codecpar;
+ int height = codec->height;
AVFrame *rgb_frame;
if (context->img_convert_frame != NULL) {
@@ -437,7 +418,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context,
(const uint8_t *const *)rgb_frame->data,
rgb_frame->linesize,
0,
- c->height,
+ codec->height,
context->current_frame->data,
context->current_frame->linesize);
}
@@ -445,9 +426,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context,
return context->current_frame;
}
-static void set_ffmpeg_property_option(AVCodecContext *c,
- IDProperty *prop,
- AVDictionary **dictionary)
+static void set_ffmpeg_property_option(IDProperty *prop, AVDictionary **dictionary)
{
char name[128];
char *param;
@@ -535,11 +514,53 @@ static void set_ffmpeg_properties(RenderData *rd,
for (curr = prop->data.group.first; curr; curr = curr->next) {
if (ffmpeg_proprty_valid(c, prop_name, curr)) {
- set_ffmpeg_property_option(c, curr, dictionary);
+ set_ffmpeg_property_option(curr, dictionary);
}
}
}
+static AVRational calc_time_base(uint den, double num, int codec_id)
+{
+ /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer
+ * (within a floating point error range).
+ * For example if we have `den = 3` and `num = 0.1` then the fps is: `den/num = 30` fps.
+ * When converting this to a FFMPEG time base, we want num to be an integer.
+ * So we simply move the decimal places of both numbers. i.e. `den = 30`, `num = 1`. */
+ float eps = FLT_EPSILON;
+ const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1;
+
+ /* Calculate the precision of the initial floating point number. */
+ if (num > 1.0) {
+ const uint num_integer_bits = log2_floor_u((unsigned int)num);
+
+ /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits)
+ * For example, a float has 23 mantissa bits and the float value 3.5f as a pow2 range of
+ * (4-2=2):
+ * (2) / pow2(23) = floating point precision for 3.5f
+ */
+ eps = (float)(1 << num_integer_bits) * FLT_EPSILON;
+ }
+
+ /* Calculate how many decimal shifts we can do until we run out of precision. */
+ const int max_num_shift = fabsf(log10f(eps));
+ /* Calculate how many times we can shift the denominator. */
+ const int max_den_shift = log10f(DENUM_MAX) - log10f(den);
+ const int max_iter = min_ii(max_num_shift, max_den_shift);
+
+ for (int i = 0; i < max_iter && fabs(num - round(num)) > eps; i++) {
+ /* Increase the number and denominator until both are integers. */
+ num *= 10;
+ den *= 10;
+ eps *= 10;
+ }
+
+ AVRational time_base;
+ time_base.den = den;
+ time_base.num = (int)num;
+
+ return time_base;
+}
+
/* prepare a video stream for the output file */
static AVStream *alloc_video_stream(FFMpegContext *context,
@@ -552,7 +573,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodecContext *c;
AVCodec *codec;
AVDictionary *opts = NULL;
@@ -566,20 +586,29 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
/* Set up the codec context */
- c = st->codec;
- c->thread_count = BLI_system_thread_count();
- c->thread_type = FF_THREAD_SLICE;
-
+ context->video_codec = avcodec_alloc_context3(NULL);
+ AVCodecContext *c = context->video_codec;
c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_VIDEO;
+ codec = avcodec_find_encoder(c->codec_id);
+ if (!codec) {
+ fprintf(stderr, "Couldn't find valid video codec\n");
+ avcodec_free_context(&c);
+ context->video_codec = NULL;
+ return NULL;
+ }
+
+ /* Load codec defaults into 'c'. */
+ avcodec_get_context_defaults3(c, codec);
+
/* Get some values from the current render settings */
c->width = rectx;
c->height = recty;
- /* FIXME: Really bad hack (tm) for NTSC support */
if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) {
+ /* FIXME: Really bad hack (tm) for NTSC support */
c->time_base.den = 2997;
c->time_base.num = 100;
}
@@ -587,21 +616,23 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
c->time_base.den = rd->frs_sec;
c->time_base.num = (int)rd->frs_sec_base;
}
- else if (compare_ff(rd->frs_sec_base, 1.001f, 0.000001f)) {
- /* This converts xx/1.001 (which is used in presets) to xx000/1001 (which is used in the rest
- * of the world, including FFmpeg). */
- c->time_base.den = (int)(rd->frs_sec * 1000);
- c->time_base.num = (int)(rd->frs_sec_base * 1000);
- }
else {
- /* This calculates a fraction (DENUM_MAX / num) which approximates the scene frame rate
- * (frs_sec / frs_sec_base). It uses the maximum denominator allowed by FFmpeg.
- */
- const double DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1;
- const double num = (DENUM_MAX / (double)rd->frs_sec) * rd->frs_sec_base;
+ c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id);
+ }
- c->time_base.den = (int)DENUM_MAX;
- c->time_base.num = (int)num;
+ /* As per the time-base documentation here:
+ * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options
+ * We want to set the time base to (1 / fps) for fixed frame rate video.
+ * If it is not possible, we want to set the time-base numbers to something as
+ * small as possible.
+ */
+ if (c->time_base.num != 1) {
+ AVRational new_time_base;
+ if (av_reduce(
+ &new_time_base.num, &new_time_base.den, c->time_base.num, c->time_base.den, INT_MAX)) {
+ /* Exact reduction was possible. Use the new value. */
+ c->time_base = new_time_base;
+ }
}
st->time_base = c->time_base;
@@ -613,6 +644,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
ffmpeg_dict_set_int(&opts, "lossless", 1);
}
else if (context->ffmpeg_crf >= 0) {
+ /* As per https://trac.ffmpeg.org/wiki/Encode/VP9 we must set the bit rate to zero when
+ * encoding with vp9 in crf mode.
+ * Set this to always be zero for other codecs as well.
+ * We don't care about bit rate in crf mode. */
+ c->bit_rate = 0;
ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf);
}
else {
@@ -652,14 +688,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
- /* Deprecated and not doing anything since July 2015, deleted in recent ffmpeg */
- // c->me_method = ME_EPZS;
-
- codec = avcodec_find_encoder(c->codec_id);
- if (!codec) {
- return NULL;
- }
-
/* Be sure to use the correct pixel format(e.g. RGB, YUV) */
if (codec->pix_fmts) {
@@ -676,12 +704,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X');
}
- if (codec_id == AV_CODEC_ID_H264) {
- /* correct wrong default ffmpeg param which crash x264 */
- c->qmin = 10;
- c->qmax = 51;
- }
-
/* Keep lossless encodes in the RGB domain. */
if (codec_id == AV_CODEC_ID_HUFFYUV) {
if (rd->im_format.planes == R_IMF_PLANES_RGBA) {
@@ -708,6 +730,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
+ /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */
+ if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && context->ffmpeg_crf == 0) {
+ c->pix_fmt = AV_PIX_FMT_YUV444P;
+ }
+
if (codec_id == AV_CODEC_ID_PNG) {
if (rd->im_format.planes == R_IMF_PLANES_RGBA) {
c->pix_fmt = AV_PIX_FMT_RGBA;
@@ -716,7 +743,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
if ((of->oformat->flags & AVFMT_GLOBALHEADER)) {
PRINT("Using global header\n");
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
/* xasp & yasp got float lately... */
@@ -727,9 +754,28 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
set_ffmpeg_properties(rd, c, "video", &opts);
- if (avcodec_open2(c, codec, &opts) < 0) {
+ if (codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ c->thread_count = 0;
+ }
+ else {
+ c->thread_count = BLI_system_thread_count();
+ }
+
+ if (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ c->thread_type = FF_THREAD_FRAME;
+ }
+ else if (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ c->thread_type = FF_THREAD_SLICE;
+ }
+
+ int ret = avcodec_open2(c, codec, &opts);
+
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't initialize video codec: %s\n", av_err2str(ret));
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
av_dict_free(&opts);
+ avcodec_free_context(&c);
+ context->video_codec = NULL;
return NULL;
}
av_dict_free(&opts);
@@ -757,6 +803,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
NULL);
}
+ avcodec_parameters_from_context(st->codecpar, c);
+
return st;
}
@@ -768,7 +816,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodecContext *c;
AVCodec *codec;
AVDictionary *opts = NULL;
@@ -780,19 +827,30 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
}
st->id = 1;
- c = st->codec;
+ context->audio_codec = avcodec_alloc_context3(NULL);
+ AVCodecContext *c = context->audio_codec;
c->thread_count = BLI_system_thread_count();
c->thread_type = FF_THREAD_SLICE;
c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_AUDIO;
+ codec = avcodec_find_encoder(c->codec_id);
+ if (!codec) {
+ fprintf(stderr, "Couldn't find valid audio codec\n");
+ avcodec_free_context(&c);
+ context->audio_codec = NULL;
+ return NULL;
+ }
+
+ /* Load codec defaults into 'c'. */
+ avcodec_get_context_defaults3(c, codec);
+
c->sample_rate = rd->ffcodecdata.audio_mixrate;
c->bit_rate = context->ffmpeg_audio_bitrate * 1000;
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->channels = rd->ffcodecdata.audio_channels;
-# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
switch (rd->ffcodecdata.audio_channels) {
case FFM_CHANNELS_MONO:
c->channel_layout = AV_CH_LAYOUT_MONO;
@@ -810,7 +868,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->channel_layout = AV_CH_LAYOUT_7POINT1;
break;
}
-# endif
if (request_float_audio_buffer(codec_id)) {
/* mainly for AAC codec which is experimental */
@@ -818,12 +875,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->sample_fmt = AV_SAMPLE_FMT_FLT;
}
- codec = avcodec_find_encoder(c->codec_id);
- if (!codec) {
- // XXX error("Couldn't find a valid audio codec");
- return NULL;
- }
-
if (codec->sample_fmts) {
/* Check if the preferred sample format for this codec is supported.
* this is because, depending on the version of libav,
@@ -832,13 +883,13 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
* Float samples in particular are not always supported. */
const enum AVSampleFormat *p = codec->sample_fmts;
for (; *p != -1; p++) {
- if (*p == st->codec->sample_fmt) {
+ if (*p == c->sample_fmt) {
break;
}
}
if (*p == -1) {
/* sample format incompatible with codec. Defaulting to a format known to work */
- st->codec->sample_fmt = codec->sample_fmts[0];
+ c->sample_fmt = codec->sample_fmts[0];
}
}
@@ -847,52 +898,48 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int best = 0;
int best_dist = INT_MAX;
for (; *p; p++) {
- int dist = abs(st->codec->sample_rate - *p);
+ int dist = abs(c->sample_rate - *p);
if (dist < best_dist) {
best_dist = dist;
best = *p;
}
}
/* best is the closest supported sample rate (same as selected if best_dist == 0) */
- st->codec->sample_rate = best;
+ c->sample_rate = best;
}
if (of->oformat->flags & AVFMT_GLOBALHEADER) {
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
set_ffmpeg_properties(rd, c, "audio", &opts);
- if (avcodec_open2(c, codec, &opts) < 0) {
- // XXX error("Couldn't initialize audio codec");
+ int ret = avcodec_open2(c, codec, &opts);
+
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret));
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
av_dict_free(&opts);
+ avcodec_free_context(&c);
+ context->audio_codec = NULL;
return NULL;
}
av_dict_free(&opts);
/* need to prevent floating point exception when using vorbis audio codec,
* initialize this value in the same way as it's done in FFmpeg itself (sergey) */
- st->codec->time_base.num = 1;
- st->codec->time_base.den = st->codec->sample_rate;
-
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- context->audio_outbuf_size = FF_MIN_BUFFER_SIZE;
-# endif
+ c->time_base.num = 1;
+ c->time_base.den = c->sample_rate;
if (c->frame_size == 0) {
/* Used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD))
* not sure if that is needed anymore, so let's try out if there are any
* complaints regarding some FFmpeg versions users might have. */
- context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels;
+ context->audio_input_samples = AV_INPUT_BUFFER_MIN_SIZE * 8 / c->bits_per_coded_sample /
+ c->channels;
}
else {
context->audio_input_samples = c->frame_size;
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size) {
- context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4;
- }
-# endif
}
context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt);
@@ -901,10 +948,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
context->audio_input_buffer = (uint8_t *)av_malloc(context->audio_input_samples * c->channels *
context->audio_sample_size);
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- context->audio_output_buffer = (uint8_t *)av_malloc(context->audio_outbuf_size);
-# endif
-
if (context->audio_deinterleave) {
context->audio_deinterleave_buffer = (uint8_t *)av_malloc(
context->audio_input_samples * c->channels * context->audio_sample_size);
@@ -912,6 +955,8 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
context->audio_time = 0.0f;
+ avcodec_parameters_from_context(st->codecpar, c);
+
return st;
}
/* essential functions -- start, append, end */
@@ -937,7 +982,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va
static void ffmpeg_add_metadata_callback(void *data,
const char *propname,
char *propvalue,
- int len)
+ int UNUSED(len))
{
AVDictionary **metadata = (AVDictionary **)data;
av_dict_set(metadata, propname, propvalue, 0);
@@ -1028,7 +1073,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
fmt->audio_codec = context->ffmpeg_audio_codec;
- BLI_strncpy(of->filename, name, sizeof(of->filename));
+ of->url = av_strdup(name);
/* set the codec to the user's selection */
switch (context->ffmpeg_type) {
case FFMPEG_AVI:
@@ -1093,9 +1138,11 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!context->video_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
+ PRINT("Video stream error: %s\n", error);
}
else {
BKE_report(reports, RPT_ERROR, "Error initializing video stream");
+ PRINT("Error initializing video stream");
}
goto fail;
}
@@ -1107,9 +1154,11 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!context->audio_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
+ PRINT("Audio stream error: %s\n", error);
}
else {
BKE_report(reports, RPT_ERROR, "Error initializing audio stream");
+ PRINT("Error initializing audio stream");
}
goto fail;
}
@@ -1117,6 +1166,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!(fmt->flags & AVFMT_NOFILE)) {
if (avio_open(&of->pb, name, AVIO_FLAG_WRITE) < 0) {
BKE_report(reports, RPT_ERROR, "Could not open file for writing");
+ PRINT("Could not open file for writing\n");
goto fail;
}
}
@@ -1126,10 +1176,12 @@ static int start_ffmpeg_impl(FFMpegContext *context,
&of->metadata, context->stamp_data, ffmpeg_add_metadata_callback, false);
}
- if (avformat_write_header(of, NULL) < 0) {
+ int ret = avformat_write_header(of, NULL);
+ if (ret < 0) {
BKE_report(reports,
RPT_ERROR,
"Could not initialize streams, probably unsupported codec combination");
+ PRINT("Could not write media header: %s\n", av_err2str(ret));
goto fail;
}
@@ -1144,13 +1196,11 @@ fail:
avio_close(of->pb);
}
- if (context->video_stream && context->video_stream->codec) {
- avcodec_close(context->video_stream->codec);
+ if (context->video_stream) {
context->video_stream = NULL;
}
- if (context->audio_stream && context->audio_stream->codec) {
- avcodec_close(context->audio_stream->codec);
+ if (context->audio_stream) {
context->audio_stream = NULL;
}
@@ -1178,46 +1228,39 @@ fail:
*/
static void flush_ffmpeg(FFMpegContext *context)
{
- int ret = 0;
+ AVCodecContext *c = context->video_codec;
+ AVPacket *packet = av_packet_alloc();
- AVCodecContext *c = context->video_stream->codec;
- /* get the delayed frames */
- while (1) {
- int got_output;
- AVPacket packet = {0};
- av_init_packet(&packet);
+ avcodec_send_frame(c, NULL);
- ret = avcodec_encode_video2(c, &packet, NULL, &got_output);
- if (ret < 0) {
- fprintf(stderr, "Error encoding delayed frame %d\n", ret);
+ /* Get the packets frames. */
+ int ret = 1;
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, packet);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets to flush. */
break;
}
- if (!got_output) {
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding delayed frame: %s\n", av_err2str(ret));
break;
}
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame PTS: %d\n", (int)packet.pts);
- }
- else {
- PRINT("Video Frame PTS: not set\n");
- }
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame DTS: %d\n", (int)packet.dts);
- }
- else {
- PRINT("Video Frame DTS: not set\n");
- }
- packet.stream_index = context->video_stream->index;
- ret = av_interleaved_write_frame(context->outfile, &packet);
- if (ret != 0) {
- fprintf(stderr, "Error writing delayed frame %d\n", ret);
+ packet->stream_index = context->video_stream->index;
+ av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(context->outfile, context->video_stream, packet);
+# endif
+
+ int write_ret = av_interleaved_write_frame(context->outfile, packet);
+ if (write_ret != 0) {
+ fprintf(stderr, "Error writing delayed frame: %s\n", av_err2str(write_ret));
break;
}
}
- avcodec_flush_buffers(context->video_stream->codec);
+
+ av_packet_free(&packet);
}
/* **********************************************************************
@@ -1315,7 +1358,8 @@ int BKE_ffmpeg_start(void *context_v,
success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
# ifdef WITH_AUDASPACE
if (context->audio_stream) {
- AVCodecContext *c = context->audio_stream->codec;
+ AVCodecContext *c = context->audio_codec;
+
AUD_DeviceSpecs specs;
specs.channels = c->channels;
@@ -1342,10 +1386,6 @@ int BKE_ffmpeg_start(void *context_v,
specs.rate = rd->ffcodecdata.audio_mixrate;
context->audio_mixdown_device = BKE_sound_mixdown(
scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume);
-# ifdef FFMPEG_CODEC_TIME_BASE
- c->time_base.den = specs.rate;
- c->time_base.num = 1;
-# endif
}
# endif
return success;
@@ -1386,8 +1426,8 @@ int BKE_ffmpeg_append(void *context_v,
// write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
if (context->video_stream) {
- avframe = generate_video_frame(context, (unsigned char *)pixels, reports);
- success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports));
+ avframe = generate_video_frame(context, (unsigned char *)pixels);
+ success = (avframe && write_video_frame(context, frame - start_frame, avframe, reports));
if (context->ffmpeg_autosplit) {
if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) {
@@ -1416,9 +1456,11 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
context->audio_mixdown_device = NULL;
}
}
+# else
+ UNUSED_VARS(is_autosplit);
# endif
- if (context->video_stream && context->video_stream->codec) {
+ if (context->video_stream) {
PRINT("Flushing delayed frames...\n");
flush_ffmpeg(context);
}
@@ -1429,14 +1471,12 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
/* Close the video codec */
- if (context->video_stream != NULL && context->video_stream->codec != NULL) {
- avcodec_close(context->video_stream->codec);
+ if (context->video_stream != NULL) {
PRINT("zero video stream %p\n", context->video_stream);
context->video_stream = NULL;
}
- if (context->audio_stream != NULL && context->audio_stream->codec != NULL) {
- avcodec_close(context->audio_stream->codec);
+ if (context->audio_stream != NULL) {
context->audio_stream = NULL;
}
@@ -1455,6 +1495,16 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
avio_close(context->outfile->pb);
}
}
+
+ if (context->video_codec != NULL) {
+ avcodec_free_context(&context->video_codec);
+ context->video_codec = NULL;
+ }
+ if (context->audio_codec != NULL) {
+ avcodec_free_context(&context->audio_codec);
+ context->audio_codec = NULL;
+ }
+
if (context->outfile != NULL) {
avformat_free_context(context->outfile);
context->outfile = NULL;
@@ -1463,12 +1513,6 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
av_free(context->audio_input_buffer);
context->audio_input_buffer = NULL;
}
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (context->audio_output_buffer != NULL) {
- av_free(context->audio_output_buffer);
- context->audio_output_buffer = NULL;
- }
-# endif
if (context->audio_deinterleave_buffer != NULL) {
av_free(context->audio_deinterleave_buffer);
@@ -1548,12 +1592,12 @@ static IDProperty *BKE_ffmpeg_property_add(RenderData *rd,
switch (o->type) {
case AV_OPT_TYPE_INT:
case AV_OPT_TYPE_INT64:
- val.i = FFMPEG_DEF_OPT_VAL_INT(o);
+ val.i = o->default_val.i64;
idp_type = IDP_INT;
break;
case AV_OPT_TYPE_DOUBLE:
case AV_OPT_TYPE_FLOAT:
- val.f = FFMPEG_DEF_OPT_VAL_DOUBLE(o);
+ val.f = o->default_val.dbl;
idp_type = IDP_FLOAT;
break;
case AV_OPT_TYPE_STRING:
@@ -1657,56 +1701,7 @@ static void ffmpeg_set_expert_options(RenderData *rd)
IDP_FreePropertyContent(rd->ffcodecdata.properties);
}
- if (codec_id == AV_CODEC_ID_H264) {
- /*
- * All options here are for x264, but must be set via ffmpeg.
- * The names are therefore different - Search for "x264 to FFmpeg option mapping"
- * to get a list.
- */
-
- /*
- * Use CABAC coder. Using "coder:1", which should be equivalent,
- * crashes Blender for some reason. Either way - this is no big deal.
- */
- BKE_ffmpeg_property_add_string(rd, "video", "coder:vlc");
-
- /*
- * The other options were taken from the libx264-default.preset
- * included in the ffmpeg distribution.
- */
-
- /* This breaks compatibility for QT. */
- // BKE_ffmpeg_property_add_string(rd, "video", "flags:loop");
- BKE_ffmpeg_property_add_string(rd, "video", "cmp:chroma");
- BKE_ffmpeg_property_add_string(rd, "video", "partitions:parti4x4"); /* Deprecated. */
- BKE_ffmpeg_property_add_string(rd, "video", "partitions:partp8x8"); /* Deprecated. */
- BKE_ffmpeg_property_add_string(rd, "video", "partitions:partb8x8"); /* Deprecated. */
- BKE_ffmpeg_property_add_string(rd, "video", "me:hex");
- BKE_ffmpeg_property_add_string(rd, "video", "subq:6");
- BKE_ffmpeg_property_add_string(rd, "video", "me_range:16");
- BKE_ffmpeg_property_add_string(rd, "video", "qdiff:4");
- BKE_ffmpeg_property_add_string(rd, "video", "keyint_min:25");
- BKE_ffmpeg_property_add_string(rd, "video", "sc_threshold:40");
- BKE_ffmpeg_property_add_string(rd, "video", "i_qfactor:0.71");
- BKE_ffmpeg_property_add_string(rd, "video", "b_strategy:1");
- BKE_ffmpeg_property_add_string(rd, "video", "bf:3");
- BKE_ffmpeg_property_add_string(rd, "video", "refs:2");
- BKE_ffmpeg_property_add_string(rd, "video", "qcomp:0.6");
-
- BKE_ffmpeg_property_add_string(rd, "video", "trellis:0");
- BKE_ffmpeg_property_add_string(rd, "video", "weightb:1");
-# ifdef FFMPEG_HAVE_DEPRECATED_FLAGS2
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:dct8x8");
- BKE_ffmpeg_property_add_string(rd, "video", "directpred:3");
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:fastpskip");
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:wpred");
-# else
- BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1");
- BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1");
- BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2");
-# endif
- }
- else if (codec_id == AV_CODEC_ID_DNXHD) {
+ if (codec_id == AV_CODEC_ID_DNXHD) {
if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) {
BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd");
}
@@ -1859,14 +1854,12 @@ bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd)
{
int codec = rd->ffcodecdata.codec;
-# ifdef FFMPEG_FFV1_ALPHA_SUPPORTED
- /* Visual Studio 2019 doesn't like #ifdef within ELEM(). */
- if (codec == AV_CODEC_ID_FFV1) {
- return true;
- }
-# endif
-
- return ELEM(codec, AV_CODEC_ID_QTRLE, AV_CODEC_ID_PNG, AV_CODEC_ID_VP9, AV_CODEC_ID_HUFFYUV);
+ return ELEM(codec,
+ AV_CODEC_ID_FFV1,
+ AV_CODEC_ID_QTRLE,
+ AV_CODEC_ID_PNG,
+ AV_CODEC_ID_VP9,
+ AV_CODEC_ID_HUFFYUV);
}
void *BKE_ffmpeg_context_create(void)
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index 706bcac4f17..71b5a74ddf7 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -82,6 +82,10 @@ typedef struct NlaEvalChannelSnapshot {
/** For an upper snapshot channel, marks values that should be blended. */
NlaValidMask blend_domain;
+ /** Only used for keyframe remapping. Any values not in the \a remap_domain will not be used
+ * for keyframe remapping. */
+ NlaValidMask remap_domain;
+
int length; /* Number of values in the property. */
bool is_base; /* Base snapshot of the channel. */
@@ -196,6 +200,13 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
const float upper_influence,
NlaEvalSnapshot *r_blended_snapshot);
+void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *lower_snapshot,
+ NlaEvalSnapshot *blended_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_upper_snapshot);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/BLI_asan.h b/source/blender/blenlib/BLI_asan.h
index a2a44e164ab..c38ad6b39d0 100644
--- a/source/blender/blenlib/BLI_asan.h
+++ b/source/blender/blenlib/BLI_asan.h
@@ -21,7 +21,7 @@
# define __has_feature(x) 0
#endif
-#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) && !defined(_MSC_VER)
# include "sanitizer/asan_interface.h"
#else
/* Ensure return value is used. Just using UNUSED_VARS results in a warning. */
diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh
index e57a5109a66..3b01bbfb86e 100644
--- a/source/blender/blenlib/BLI_color.hh
+++ b/source/blender/blenlib/BLI_color.hh
@@ -22,41 +22,122 @@
namespace blender {
-struct Color4f {
- float r, g, b, a;
+/**
+ * CPP based color structures.
+ *
+ * Strongly typed color storage structures with space and alpha association.
+ * Will increase readability and visibility of typical mistakes when
+ * working with colors.
+ *
+ * The storage structs can hold 4 channels (r, g, b and a).
+ *
+ * Usage:
+ *
+ * Convert a theme byte color to a linearrgb premultiplied.
+ * ```
+ * ColorTheme4b theme_color;
+ * ColorSceneLinear4f<eAlpha::Premultiplied> linearrgb_color =
+ * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha();
+ * ```
+ *
+ * The API is structured to make most use of inlining. Most notable are space
+ * conversions done via `BLI_color_convert_to*` functions.
+ *
+ * - Conversions between spaces (theme <=> scene linear) should always be done by
+ * invoking the `BLI_color_convert_to*` methods.
+ * - Encoding colors (compressing to store colors inside a less precision storage)
+ * should be done by invoking the `encode` and `decode` methods.
+ * - Changing alpha association should be done by invoking `premultiply_alpha` or
+ * `unpremultiply_alpha` methods.
+ *
+ * # Encoding.
+ *
+ * Color encoding is used to store colors with less precision as in using `uint8_t` in
+ * stead of `float`. This encoding is supported for `eSpace::SceneLinear`.
+ * To make this clear to the developer the `eSpace::SceneLinearByteEncoded`
+ * space is added.
+ *
+ * # Precision
+ *
+ * Colors can be stored using `uint8_t` or `float` colors. The conversion
+ * between the two precisions are available as methods. (`to_4b` and
+ * `to_4f`).
+ *
+ * # Alpha conversion
+ *
+ * Alpha conversion is only supported in SceneLinear space.
+ *
+ * Extending this file:
+ * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations
+ * of rgb based colors. `ColorHsl4f<eSpace::SceneLinear, eAlpha::Premultiplied>`
+ * - Add non RGB spaces/storages ColorXyz.
+ */
+
+/* Enumeration containing the different alpha modes. */
+enum class eAlpha {
+ /* Color and alpha are unassociated. */
+ Straight,
+ /* Color and alpha are associated. */
+ Premultiplied,
+};
+std::ostream &operator<<(std::ostream &stream, const eAlpha &space);
- Color4f() = default;
+/* Enumeration containing internal spaces. */
+enum class eSpace {
+ /* Blender theme color space (sRGB). */
+ Theme,
+ /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */
+ SceneLinear,
+ /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */
+ SceneLinearByteEncoded,
+};
+std::ostream &operator<<(std::ostream &stream, const eSpace &space);
- Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3])
+/* Template class to store RGBA values with different precision, space and alpha association. */
+template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA {
+ public:
+ ChannelStorageType r, g, b, a;
+ constexpr ColorRGBA() = default;
+
+ constexpr ColorRGBA(const ChannelStorageType rgba[4])
+ : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3])
{
}
- Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a)
+ constexpr ColorRGBA(const ChannelStorageType r,
+ const ChannelStorageType g,
+ const ChannelStorageType b,
+ const ChannelStorageType a)
+ : r(r), g(g), b(b), a(a)
{
}
- operator float *()
+ operator ChannelStorageType *()
{
return &r;
}
- operator const float *() const
+ operator const ChannelStorageType *() const
{
return &r;
}
- friend std::ostream &operator<<(std::ostream &stream, Color4f c)
+ friend std::ostream &operator<<(std::ostream &stream,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &c)
{
- stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
+
+ stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
return stream;
}
- friend bool operator==(const Color4f &a, const Color4f &b)
+ friend bool operator==(const ColorRGBA<ChannelStorageType, Space, Alpha> &a,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &b)
{
return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
}
- friend bool operator!=(const Color4f &a, const Color4f &b)
+ friend bool operator!=(const ColorRGBA<ChannelStorageType, Space, Alpha> &a,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &b)
{
return !(a == b);
}
@@ -71,58 +152,209 @@ struct Color4f {
}
};
-struct Color4b {
- uint8_t r, g, b, a;
+/* Forward declarations of concrete color classes. */
+template<eAlpha Alpha> class ColorSceneLinear4f;
+template<eAlpha Alpha> class ColorSceneLinearByteEncoded4b;
+template<typename ChannelStorageType> class ColorTheme4;
- Color4b() = default;
+/* Forward declaration of precision conversion methods. */
+BLI_INLINE ColorTheme4<float> BLI_color_convert_to_theme4f(const ColorTheme4<uint8_t> &srgb4b);
+BLI_INLINE ColorTheme4<uint8_t> BLI_color_convert_to_theme4b(const ColorTheme4<float> &srgb4f);
- Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a)
+template<eAlpha Alpha>
+class ColorSceneLinear4f final : public ColorRGBA<float, eSpace::SceneLinear, Alpha> {
+ public:
+ constexpr ColorSceneLinear4f<Alpha>() : ColorRGBA<float, eSpace::SceneLinear, Alpha>()
{
}
- Color4b(Color4f other)
+ constexpr ColorSceneLinear4f<Alpha>(const float *rgba)
+ : ColorRGBA<float, eSpace::SceneLinear, Alpha>(rgba)
{
- rgba_float_to_uchar(*this, other);
}
- operator Color4f() const
+ constexpr ColorSceneLinear4f<Alpha>(float r, float g, float b, float a)
+ : ColorRGBA<float, eSpace::SceneLinear, Alpha>(r, g, b, a)
{
- Color4f result;
- rgba_uchar_to_float(result, *this);
- return result;
}
- operator uint8_t *()
+ /**
+ * Convert to its byte encoded counter space.
+ **/
+ ColorSceneLinearByteEncoded4b<Alpha> encode() const
{
- return &r;
+ ColorSceneLinearByteEncoded4b<Alpha> encoded;
+ linearrgb_to_srgb_uchar4(encoded, *this);
+ return encoded;
}
- operator const uint8_t *() const
+ /**
+ * Convert color and alpha association to premultiplied alpha.
+ *
+ * Does nothing when color has already a premultiplied alpha.
+ */
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiply_alpha() const
{
- return &r;
+ if constexpr (Alpha == eAlpha::Straight) {
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied;
+ straight_to_premul_v4_v4(premultiplied, *this);
+ return premultiplied;
+ }
+ else {
+ return *this;
+ }
}
- friend std::ostream &operator<<(std::ostream &stream, Color4b c)
+ /**
+ * Convert color and alpha association to straight alpha.
+ *
+ * Does nothing when color has straighten alpha.
+ */
+ ColorSceneLinear4f<eAlpha::Straight> unpremultiply_alpha() const
{
- stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
- return stream;
+ if constexpr (Alpha == eAlpha::Premultiplied) {
+ ColorSceneLinear4f<eAlpha::Straight> straighten;
+ premul_to_straight_v4_v4(straighten, *this);
+ return straighten;
+ }
+ else {
+ return *this;
+ }
}
+};
- friend bool operator==(const Color4b &a, const Color4b &b)
+template<eAlpha Alpha>
+class ColorSceneLinearByteEncoded4b final
+ : public ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha> {
+ public:
+ constexpr ColorSceneLinearByteEncoded4b() = default;
+
+ constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba)
+ : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(rgba)
{
- return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
}
- friend bool operator!=(const Color4b &a, const Color4b &b)
+ constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+ : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(r, g, b, a)
{
- return !(a == b);
}
- uint64_t hash() const
+ /**
+ * Convert to back to float color.
+ */
+ ColorSceneLinear4f<Alpha> decode() const
+ {
+ ColorSceneLinear4f<Alpha> decoded;
+ srgb_to_linearrgb_uchar4(decoded, *this);
+ return decoded;
+ }
+};
+
+/**
+ * Theme color template class.
+ *
+ * Don't use directly, but use `ColorTheme4b/ColorTheme4b`.
+ *
+ * This has been implemented as a template to improve inlining. When implemented as concrete
+ * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be
+ * inlined.
+ */
+template<typename ChannelStorageType>
+class ColorTheme4 final : public ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight> {
+ public:
+ constexpr ColorTheme4() : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(){};
+
+ constexpr ColorTheme4(const ChannelStorageType *rgba)
+ : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(rgba)
+ {
+ }
+
+ constexpr ColorTheme4(ChannelStorageType r,
+ ChannelStorageType g,
+ ChannelStorageType b,
+ ChannelStorageType a)
+ : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(r, g, b, a)
+ {
+ }
+
+ /**
+ * Change precision of color to float.
+ */
+ ColorTheme4<float> to_4f() const
{
- return static_cast<uint64_t>(r * 1283591) ^ static_cast<uint64_t>(g * 850177) ^
- static_cast<uint64_t>(b * 735391) ^ static_cast<uint64_t>(a * 442319);
+ if constexpr ((std::is_same_v<ChannelStorageType, uint8_t>)) {
+ return BLI_color_convert_to_theme4f(*this);
+ }
+ else {
+ return *this;
+ }
+ }
+
+ /**
+ * Change precision of color to uint8_t.
+ */
+ ColorTheme4<uint8_t> to_4b() const
+ {
+ if constexpr ((std::is_same_v<ChannelStorageType, float>)) {
+ return BLI_color_convert_to_theme4b(*this);
+ }
+ else {
+ return *this;
+ }
}
};
+using ColorTheme4b = ColorTheme4<uint8_t>;
+using ColorTheme4f = ColorTheme4<float>;
+
+BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f)
+{
+ ColorTheme4b theme4b;
+ rgba_float_to_uchar(theme4b, theme4f);
+ return theme4b;
+}
+
+BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b)
+{
+ ColorTheme4f theme4f;
+ rgba_uchar_to_float(theme4f, theme4b);
+ return theme4f;
+}
+
+BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear(
+ const ColorTheme4f &theme4f)
+{
+ ColorSceneLinear4f<eAlpha::Straight> scene_linear;
+ srgb_to_linearrgb_v4(scene_linear, theme4f);
+ return scene_linear;
+}
+
+BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear(
+ const ColorTheme4b &theme4b)
+{
+ ColorSceneLinear4f<eAlpha::Straight> scene_linear;
+ srgb_to_linearrgb_uchar4(scene_linear, theme4b);
+ return scene_linear;
+}
+
+BLI_INLINE ColorTheme4f
+BLI_color_convert_to_theme4f(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear)
+{
+ ColorTheme4f theme4f;
+ linearrgb_to_srgb_v4(theme4f, scene_linear);
+ return theme4f;
+}
+
+BLI_INLINE ColorTheme4b
+BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear)
+{
+ ColorTheme4b theme4b;
+ linearrgb_to_srgb_uchar4(theme4b, scene_linear);
+ return theme4b;
+}
+
+/* Internal roles. For convenience to shorten the type names and hide complexity. */
+using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>;
+using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>;
+
} // namespace blender
diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h
index 680c4bc78da..4b5a7d671f2 100644
--- a/source/blender/blenlib/BLI_compiler_attrs.h
+++ b/source/blender/blenlib/BLI_compiler_attrs.h
@@ -98,3 +98,10 @@
#else
# define ATTR_ALIGN(x) __attribute__((aligned(x)))
#endif
+
+/* Alignment directive */
+#ifdef _WIN64
+# define ALIGN_STRUCT __declspec(align(64))
+#else
+# define ALIGN_STRUCT
+#endif
diff --git a/source/blender/blenlib/BLI_endian_defines.h b/source/blender/blenlib/BLI_endian_defines.h
new file mode 100644
index 00000000000..31f28572c79
--- /dev/null
+++ b/source/blender/blenlib/BLI_endian_defines.h
@@ -0,0 +1,38 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+/* NOTE: these names are historic and could use a more generic prefix.
+ * This could be done as part of a bigger refactor. */
+
+/** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */
+#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
+# error Either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ must be defined.
+#endif
+
+#define L_ENDIAN 1
+#define B_ENDIAN 0
+
+#ifdef __BIG_ENDIAN__
+# define ENDIAN_ORDER B_ENDIAN
+#else
+# define ENDIAN_ORDER L_ENDIAN
+#endif
diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
new file mode 100644
index 00000000000..89be4cad848
--- /dev/null
+++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
@@ -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.
+ */
+
+#pragma once
+
+#ifdef WITH_TBB
+# include <tbb/enumerable_thread_specific.h>
+#endif
+
+#include <atomic>
+#include <mutex>
+
+#include "BLI_map.hh"
+#include "BLI_utility_mixins.hh"
+
+namespace blender {
+
+namespace enumerable_thread_specific_utils {
+inline std::atomic<int> next_id = 0;
+inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed);
+} // namespace enumerable_thread_specific_utils
+
+/**
+ * This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we
+ * want to be able to build without tbb.
+ *
+ * More features of the tbb version can be wrapped when they are used.
+ */
+template<typename T> class EnumerableThreadSpecific : NonCopyable, NonMovable {
+#ifdef WITH_TBB
+
+ private:
+ tbb::enumerable_thread_specific<T> values_;
+
+ public:
+ T &local()
+ {
+ return values_.local();
+ }
+
+#else /* WITH_TBB */
+
+ private:
+ std::mutex mutex_;
+ /* Maps thread ids to their corresponding values. The values are not embedded in the map, so that
+ * their addresses do not change when the map grows. */
+ Map<int, std::unique_ptr<T>> values_;
+
+ public:
+ T &local()
+ {
+ const int thread_id = enumerable_thread_specific_utils::thread_id;
+ std::lock_guard lock{mutex_};
+ return *values_.lookup_or_add_cb(thread_id, []() { return std::make_unique<T>(); });
+ }
+
+#endif /* WITH_TBB */
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index df80e720363..7cfecc798a7 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -34,6 +34,7 @@
#include <limits.h> /* for PATH_MAX */
#include "BLI_compiler_attrs.h"
+#include "BLI_fileops_types.h"
#include "BLI_utildefines.h"
#ifdef __cplusplus
@@ -87,7 +88,7 @@ typedef enum eFileAttributes {
FILE_ATTR_RESTRICTED = 1 << 6, /* Protected by OS. */
FILE_ATTR_TEMPORARY = 1 << 7, /* Used for temporary storage. */
FILE_ATTR_SPARSE_FILE = 1 << 8, /* Sparse File. */
- FILE_ATTR_OFFLINE = 1 << 9, /* Data is not immediately available. */
+ FILE_ATTR_OFFLINE = 1 << 9, /* Contents available after a short delay. */
FILE_ATTR_ALIAS = 1 << 10, /* Mac Alias or Windows LNK. File-based redirection. */
FILE_ATTR_REPARSE_POINT = 1 << 11, /* File has associated re-parse point. */
FILE_ATTR_SYMLINK = 1 << 12, /* Reference to another file. */
@@ -125,15 +126,20 @@ void BLI_filelist_free(struct direntry *filelist, const unsigned int nrentries);
void BLI_filelist_entry_size_to_string(const struct stat *st,
const uint64_t sz,
const bool compact,
- char r_size[]);
-void BLI_filelist_entry_mode_to_string(
- const struct stat *st, const bool compact, char r_mode1[], char r_mode2[], char r_mode3[]);
-void BLI_filelist_entry_owner_to_string(const struct stat *st, const bool compact, char r_owner[]);
+ char r_size[FILELIST_DIRENTRY_SIZE_LEN]);
+void BLI_filelist_entry_mode_to_string(const struct stat *st,
+ const bool compact,
+ char r_mode1[FILELIST_DIRENTRY_MODE_LEN],
+ char r_mode2[FILELIST_DIRENTRY_MODE_LEN],
+ char r_mode3[FILELIST_DIRENTRY_MODE_LEN]);
+void BLI_filelist_entry_owner_to_string(const struct stat *st,
+ const bool compact,
+ char r_owner[FILELIST_DIRENTRY_OWNER_LEN]);
void BLI_filelist_entry_datetime_to_string(const struct stat *st,
const int64_t ts,
const bool compact,
- char r_time[],
- char r_date[],
+ char r_time[FILELIST_DIRENTRY_TIME_LEN],
+ char r_date[FILELIST_DIRENTRY_DATE_LEN],
bool *r_is_today,
bool *r_is_yesterday);
diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh
index cbc4d4ed366..04aae375889 100644
--- a/source/blender/blenlib/BLI_float3.hh
+++ b/source/blender/blenlib/BLI_float3.hh
@@ -245,6 +245,13 @@ struct float3 {
return result;
}
+ static float3 cross(const float3 &a, const float3 &b)
+ {
+ float3 result;
+ cross_v3_v3v3(result, a, b);
+ return result;
+ }
+
static float3 project(const float3 &a, const float3 &b)
{
float3 result;
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index f5ba91bc12c..396b0b1bd21 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -45,6 +45,37 @@ struct float4x4 {
return mat;
}
+ static float4x4 from_normalized_axis_data(const float3 location,
+ const float3 forward,
+ const float3 up)
+ {
+ BLI_ASSERT_UNIT_V3(forward);
+ BLI_ASSERT_UNIT_V3(up);
+ float4x4 matrix;
+ const float3 cross = float3::cross(forward, up);
+ matrix.values[0][0] = forward.x;
+ matrix.values[1][0] = cross.x;
+ matrix.values[2][0] = up.x;
+ matrix.values[3][0] = location.x;
+
+ matrix.values[0][1] = forward.y;
+ matrix.values[1][1] = cross.y;
+ matrix.values[2][1] = up.y;
+ matrix.values[3][1] = location.y;
+
+ matrix.values[0][2] = forward.z;
+ matrix.values[1][2] = cross.z;
+ matrix.values[2][2] = up.z;
+ matrix.values[3][2] = location.z;
+
+ matrix.values[0][3] = 0.0f;
+ matrix.values[1][3] = 0.0f;
+ matrix.values[2][3] = 0.0f;
+ matrix.values[3][3] = 1.0f;
+
+ return matrix;
+ }
+
static float4x4 identity()
{
float4x4 mat;
@@ -116,6 +147,19 @@ struct float4x4 {
return scale;
}
+ void apply_scale(const float scale)
+ {
+ values[0][0] *= scale;
+ values[0][1] *= scale;
+ values[0][2] *= scale;
+ values[1][0] *= scale;
+ values[1][1] *= scale;
+ values[1][2] *= scale;
+ values[2][0] *= scale;
+ values[2][1] *= scale;
+ values[2][2] *= scale;
+ }
+
float4x4 inverted() const
{
float4x4 result;
diff --git a/source/blender/blenlib/BLI_function_ref.hh b/source/blender/blenlib/BLI_function_ref.hh
index 57fffdc09b4..38e1ba593c5 100644
--- a/source/blender/blenlib/BLI_function_ref.hh
+++ b/source/blender/blenlib/BLI_function_ref.hh
@@ -16,6 +16,7 @@
#pragma once
+#include <optional>
#include <type_traits>
#include <utility>
@@ -139,6 +140,29 @@ template<typename Ret, typename... Params> class FunctionRef<Ret(Params...)> {
return callback_(callable_, std::forward<Params>(params)...);
}
+ using OptionalReturnValue = std::conditional_t<std::is_void_v<Ret>, void, std::optional<Ret>>;
+
+ /**
+ * Calls the referenced function if it is available.
+ * The return value is of type `std::optional<Ret>` if `Ret` is not `void`.
+ * Otherwise the return type is `void`.
+ */
+ OptionalReturnValue call_safe(Params... params) const
+ {
+ if constexpr (std::is_void_v<Ret>) {
+ if (callback_ == nullptr) {
+ return;
+ }
+ callback_(callable_, std::forward<Params>(params)...);
+ }
+ else {
+ if (callback_ == nullptr) {
+ return {};
+ }
+ return callback_(callable_, std::forward<Params>(params)...);
+ }
+ }
+
/**
* Returns true, when the `FunctionRef` references a function currently.
* If this returns false, the `FunctionRef` must not be called.
diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh
index ac0c2e4260d..fbed321534c 100644
--- a/source/blender/blenlib/BLI_hash.hh
+++ b/source/blender/blenlib/BLI_hash.hh
@@ -85,9 +85,12 @@
namespace blender {
/**
- * If there is no other specialization of #DefaultHash for a given type, try to call `hash()` on
- * the value. If there is no such method, this will result in a compiler error. Usually that means
- * that you have to implement a hash function using one of three strategies listed above.
+ * If there is no other specialization of #DefaultHash for a given type, look for a hash function
+ * on the type itself. Implementing a `hash()` method on a type is often significantly easier than
+ * specializing #DefaultHash.
+ *
+ * To support heterogeneous lookup, a type can also implement a static `hash_as(const OtherType &)`
+ * function.
*
* In the case of an enum type, the default hash is just to cast the enum value to an integer.
*/
@@ -95,15 +98,23 @@ template<typename T> struct DefaultHash {
uint64_t operator()(const T &value) const
{
if constexpr (std::is_enum_v<T>) {
+ /* For enums use the value as hash directly. */
return (uint64_t)value;
}
else {
+ /* Try to call the `hash()` function on the value. */
+ /* If this results in a compiler error, no hash function for the type has been found. */
return value.hash();
}
}
template<typename U> uint64_t operator()(const U &value) const
{
+ /* Try calling the static `T::hash_as(value)` function with the given value. The returned hash
+ * should be "compatible" with `T::hash()`. Usually that means that if `value` is converted to
+ * `T` its hash does not change. */
+ /* If this results in a compiler error, no hash function for the heterogeneous lookup has been
+ * found. */
return T::hash_as(value);
}
};
diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh
index 48b01edcd6f..f04c0e9c80a 100644
--- a/source/blender/blenlib/BLI_index_mask.hh
+++ b/source/blender/blenlib/BLI_index_mask.hh
@@ -110,7 +110,7 @@ class IndexMask {
}
/**
- * Returns the n-th index referenced by this IndexMask. The `index_mask` method returns an
+ * Returns the n-th index referenced by this IndexMask. The `index_range` method returns an
* IndexRange containing all indices that can be used as parameter here.
*/
int64_t operator[](int64_t n) const
diff --git a/source/blender/blenlib/BLI_iterator.h b/source/blender/blenlib/BLI_iterator.h
index c1cd1c21dac..198e42f340d 100644
--- a/source/blender/blenlib/BLI_iterator.h
+++ b/source/blender/blenlib/BLI_iterator.h
@@ -34,13 +34,19 @@ typedef struct BLI_Iterator {
typedef void (*IteratorCb)(BLI_Iterator *iter);
typedef void (*IteratorBeginCb)(BLI_Iterator *iter, void *data_in);
+#define BLI_ITERATOR_INIT(iter) \
+ { \
+ (iter)->skip = false; \
+ (iter)->valid = true; \
+ } \
+ ((void)0)
+
#define ITER_BEGIN(callback_begin, callback_next, callback_end, _data_in, _type, _instance) \
{ \
_type _instance; \
IteratorCb callback_end_func = callback_end; \
BLI_Iterator iter_macro; \
- iter_macro.skip = false; \
- iter_macro.valid = true; \
+ BLI_ITERATOR_INIT(&iter_macro); \
for (callback_begin(&iter_macro, (_data_in)); iter_macro.valid; callback_next(&iter_macro)) { \
if (iter_macro.skip) { \
iter_macro.skip = false; \
diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh
index 47705b1d40b..7de6bcfdd98 100644
--- a/source/blender/blenlib/BLI_linear_allocator.hh
+++ b/source/blender/blenlib/BLI_linear_allocator.hh
@@ -129,10 +129,28 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
}
/**
+ * Construct multiple instances of a type in an array. The constructor of is called with the
+ * given arguments. The caller is responsible for calling the destructor (and not `delete`) on
+ * the constructed elements.
+ */
+ template<typename T, typename... Args>
+ MutableSpan<T> construct_array(int64_t size, Args &&... args)
+ {
+ MutableSpan<T> array = this->allocate_array<T>(size);
+ for (const int64_t i : IndexRange(size)) {
+ new (&array[i]) T(std::forward<Args>(args)...);
+ }
+ return array;
+ }
+
+ /**
* Copy the given array into a memory buffer provided by this allocator.
*/
template<typename T> MutableSpan<T> construct_array_copy(Span<T> src)
{
+ if (src.is_empty()) {
+ return {};
+ }
MutableSpan<T> dst = this->allocate_array<T>(src.size());
uninitialized_copy_n(src.data(), src.size(), dst.data());
return dst;
diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh
index 9fa69853e44..4d254960f34 100644
--- a/source/blender/blenlib/BLI_map.hh
+++ b/source/blender/blenlib/BLI_map.hh
@@ -247,11 +247,11 @@ class Map {
{
this->add_new_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new_as(ForwardKey &&key, ForwardValue &&... value)
{
this->add_new__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -277,11 +277,11 @@ class Map {
{
return this->add_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->add__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -307,11 +307,11 @@ class Map {
{
return this->add_overwrite_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_overwrite_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->add_overwrite__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -413,12 +413,12 @@ class Map {
{
return this->pop_default_as(key, std::move(default_value));
}
- template<typename ForwardKey, typename ForwardValue>
- Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value pop_default_as(const ForwardKey &key, ForwardValue &&... default_value)
{
Slot *slot = this->lookup_slot_ptr(key, hash_(key));
if (slot == nullptr) {
- return std::forward<ForwardValue>(default_value);
+ return Value(std::forward<ForwardValue>(default_value)...);
}
Value value = std::move(*slot->value());
slot->remove();
@@ -525,15 +525,15 @@ class Map {
{
return this->lookup_default_as(key, default_value);
}
- template<typename ForwardKey, typename ForwardValue>
- Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const
+ template<typename ForwardKey, typename... ForwardValue>
+ Value lookup_default_as(const ForwardKey &key, ForwardValue &&... default_value) const
{
const Value *ptr = this->lookup_ptr_as(key);
if (ptr != nullptr) {
return *ptr;
}
else {
- return std::forward<ForwardValue>(default_value);
+ return Value(std::forward<ForwardValue>(default_value)...);
}
}
@@ -557,11 +557,11 @@ class Map {
{
return this->lookup_or_add_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->lookup_or_add__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -605,6 +605,37 @@ class Map {
}
/**
+ * Returns the key that is stored in the set that compares equal to the given key. This invokes
+ * undefined behavior when the key is not in the map.
+ */
+ const Key &lookup_key(const Key &key) const
+ {
+ return this->lookup_key_as(key);
+ }
+ template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const
+ {
+ const Slot &slot = this->lookup_slot(key, hash_(key));
+ return *slot.key();
+ }
+
+ /**
+ * Returns a pointer to the key that is stored in the map that compares equal to the given key.
+ * If the key is not in the map, null is returned.
+ */
+ const Key *lookup_key_ptr(const Key &key) const
+ {
+ return this->lookup_key_ptr_as(key);
+ }
+ template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const
+ {
+ const Slot *slot = this->lookup_slot_ptr(key, hash_(key));
+ if (slot == nullptr) {
+ return nullptr;
+ }
+ return slot->key();
+ }
+
+ /**
* Calls the provided callback for every key-value-pair in the map. The callback is expected
* to take a `const Key &` as first and a `const Value &` as second parameter.
*/
@@ -621,19 +652,23 @@ class Map {
}
}
- /**
- * A utility iterator that reduces the amount of code when implementing the actual iterators.
- * This uses the "curiously recurring template pattern" (CRTP).
- */
- template<typename SubIterator> struct BaseIterator {
+ /* Common base class for all iterators below. */
+ struct BaseIterator {
+ public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
+ protected:
+ /* We could have separate base iterators for const and non-const iterators, but that would add
+ * more complexity than benefits right now. */
Slot *slots_;
int64_t total_slots_;
int64_t current_slot_;
- BaseIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
+ friend Map;
+
+ public:
+ BaseIterator(const Slot *slots, const int64_t total_slots, const int64_t current_slot)
: slots_(const_cast<Slot *>(slots)), total_slots_(total_slots), current_slot_(current_slot)
{
}
@@ -667,11 +702,29 @@ class Map {
return !(a != b);
}
+ protected:
+ Slot &current_slot() const
+ {
+ return slots_[current_slot_];
+ }
+ };
+
+ /**
+ * A utility iterator that reduces the amount of code when implementing the actual iterators.
+ * This uses the "curiously recurring template pattern" (CRTP).
+ */
+ template<typename SubIterator> class BaseIteratorRange : public BaseIterator {
+ public:
+ BaseIteratorRange(const Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIterator(slots, total_slots, current_slot)
+ {
+ }
+
SubIterator begin() const
{
- for (int64_t i = 0; i < total_slots_; i++) {
- if (slots_[i].is_occupied()) {
- return SubIterator(slots_, total_slots_, i);
+ for (int64_t i = 0; i < this->total_slots_; i++) {
+ if (this->slots_[i].is_occupied()) {
+ return SubIterator(this->slots_, this->total_slots_, i);
}
}
return this->end();
@@ -679,23 +732,18 @@ class Map {
SubIterator end() const
{
- return SubIterator(slots_, total_slots_, total_slots_);
- }
-
- Slot &current_slot() const
- {
- return slots_[current_slot_];
+ return SubIterator(this->slots_, this->total_slots_, this->total_slots_);
}
};
- class KeyIterator final : public BaseIterator<KeyIterator> {
+ class KeyIterator final : public BaseIteratorRange<KeyIterator> {
public:
using value_type = Key;
using pointer = const Key *;
using reference = const Key &;
KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<KeyIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<KeyIterator>(slots, total_slots, current_slot)
{
}
@@ -705,14 +753,14 @@ class Map {
}
};
- class ValueIterator final : public BaseIterator<ValueIterator> {
+ class ValueIterator final : public BaseIteratorRange<ValueIterator> {
public:
using value_type = Value;
using pointer = const Value *;
using reference = const Value &;
ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<ValueIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<ValueIterator>(slots, total_slots, current_slot)
{
}
@@ -722,14 +770,14 @@ class Map {
}
};
- class MutableValueIterator final : public BaseIterator<MutableValueIterator> {
+ class MutableValueIterator final : public BaseIteratorRange<MutableValueIterator> {
public:
using value_type = Value;
using pointer = Value *;
using reference = Value &;
- MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<MutableValueIterator>(slots, total_slots, current_slot)
+ MutableValueIterator(Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIteratorRange<MutableValueIterator>(slots, total_slots, current_slot)
{
}
@@ -754,14 +802,14 @@ class Map {
}
};
- class ItemIterator final : public BaseIterator<ItemIterator> {
+ class ItemIterator final : public BaseIteratorRange<ItemIterator> {
public:
using value_type = Item;
using pointer = Item *;
using reference = Item &;
ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<ItemIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<ItemIterator>(slots, total_slots, current_slot)
{
}
@@ -772,14 +820,14 @@ class Map {
}
};
- class MutableItemIterator final : public BaseIterator<MutableItemIterator> {
+ class MutableItemIterator final : public BaseIteratorRange<MutableItemIterator> {
public:
using value_type = MutableItem;
using pointer = MutableItem *;
using reference = MutableItem &;
- MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<MutableItemIterator>(slots, total_slots, current_slot)
+ MutableItemIterator(Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIteratorRange<MutableItemIterator>(slots, total_slots, current_slot)
{
}
@@ -840,6 +888,19 @@ class Map {
}
/**
+ * Remove the key-value-pair that the iterator is currently pointing at.
+ * It is valid to call this method while iterating over the map. However, after this method has
+ * been called, the removed element must not be accessed anymore.
+ */
+ void remove(const BaseIterator &iterator)
+ {
+ Slot &slot = iterator.current_slot();
+ BLI_assert(slot.is_occupied());
+ slot.remove();
+ removed_slots_++;
+ }
+
+ /**
* Print common statistics like size and collision count. This is useful for debugging purposes.
*/
void print_stats(StringRef name = "") const
@@ -982,7 +1043,7 @@ class Map {
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
if (slot.is_empty()) {
- slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash);
+ slot.occupy(std::move(*old_slot.key()), hash, std::move(*old_slot.value()));
return;
}
}
@@ -996,8 +1057,8 @@ class Map {
new (this) Map(NoExceptConstructor(), allocator);
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->contains_as(key));
@@ -1005,7 +1066,7 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return;
}
@@ -1013,14 +1074,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- bool add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return true;
}
@@ -1075,7 +1136,7 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), create_value(), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, create_value());
occupied_and_removed_slots_++;
return *slot.value();
}
@@ -1086,14 +1147,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return *slot.value();
}
@@ -1104,15 +1165,15 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
auto create_func = [&](Value *ptr) {
- new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value));
+ new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)...);
return true;
};
auto modify_func = [&](Value *ptr) {
- *ptr = std::forward<ForwardValue>(value);
+ *ptr = Value(std::forward<ForwardValue>(value)...);
return false;
};
return this->add_or_modify__impl(
@@ -1221,16 +1282,18 @@ template<typename Key, typename Value> class StdUnorderedMapWrapper {
map_.reserve(n);
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new(ForwardKey &&key, ForwardValue &&... value)
{
- map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)});
+ map_.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)});
}
- template<typename ForwardKey, typename ForwardValue>
- bool add(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add(ForwardKey &&key, ForwardValue &&... value)
{
- return map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second;
+ return map_
+ .insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)})
+ .second;
}
bool contains(const Key &key) const
diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh
index c0cb3091a8e..1b4ca11af41 100644
--- a/source/blender/blenlib/BLI_map_slots.hh
+++ b/source/blender/blenlib/BLI_map_slots.hh
@@ -195,11 +195,11 @@ template<typename Key, typename Value> class SimpleMapSlot {
* Change the state of this slot from empty/removed to occupied. The key/value has to be
* constructed by calling the constructor with the given key/value as parameter.
*/
- template<typename ForwardKey, typename ForwardValue>
- void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->is_occupied());
- new (&value_buffer_) Value(std::forward<ForwardValue>(value));
+ new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
state_ = Occupied;
}
@@ -315,12 +315,12 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot
return is_equal(key, key_);
}
- template<typename ForwardKey, typename ForwardValue>
- void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- new (&value_buffer_) Value(std::forward<ForwardValue>(value));
+ new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
}
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index 028ca31a059..46219ad5493 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -154,6 +154,9 @@ MINLINE int max_iii(int a, int b, int c);
MINLINE int min_iiii(int a, int b, int c, int d);
MINLINE int max_iiii(int a, int b, int c, int d);
+MINLINE uint min_uu(uint a, uint b);
+MINLINE uint max_uu(uint a, uint b);
+
MINLINE size_t min_zz(size_t a, size_t b);
MINLINE size_t max_zz(size_t a, size_t b);
diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h
index 26d2f1fcb29..28257ba418a 100644
--- a/source/blender/blenlib/BLI_math_color.h
+++ b/source/blender/blenlib/BLI_math_color.h
@@ -105,8 +105,8 @@ int constrain_rgb(float *r, float *g, float *b);
void minmax_rgb(short c[3]);
void hsv_clamp_v(float hsv[3], float v_max);
-void rgb_float_set_hue_float_offset(float *rgb, float hue_offset);
-void rgb_byte_set_hue_float_offset(unsigned char *rgb, float hue_offset);
+void rgb_float_set_hue_float_offset(float rgb[3], float hue_offset);
+void rgb_byte_set_hue_float_offset(unsigned char rgb[3], float hue_offset);
void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3]);
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4]);
@@ -143,6 +143,7 @@ MINLINE void rgba_uchar_args_test_set(unsigned char col[4],
MINLINE void cpack_cpy_3ub(unsigned char r_col[3], const unsigned int pack);
void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, float max);
+void wavelength_to_xyz_table(float *r_table, int width);
/********* lift/gamma/gain / ASC-CDL conversion ***********/
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index d767c2924d1..c744c5d13d3 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -119,10 +119,10 @@ float dist_signed_to_plane_v3(const float p[3], const float plane[4]);
float dist_to_plane_v3(const float p[3], const float plane[4]);
/* plane3 versions */
-float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_squared_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_signed_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_to_plane3_v3(const float p[3], const float plane[4]);
+float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_squared_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_signed_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_to_plane3_v3(const float p[3], const float plane[3]);
float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
@@ -778,7 +778,7 @@ MINLINE float dot_shsh(const float a[9], const float b[9]);
MINLINE float eval_shv3(float r[9], const float v[3]);
MINLINE float diffuse_shv3(float r[9], const float v[3]);
MINLINE void vec_fac_to_sh(float r[9], const float v[3], const float f);
-MINLINE void madd_sh_shfl(float r[9], const float sh[3], const float f);
+MINLINE void madd_sh_shfl(float r[9], const float sh[9], const float f);
/********************************* Form Factor *******************************/
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 6324963f06a..54df88ca541 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -266,6 +266,9 @@ void orthogonalize_m4(float R[4][4], int axis);
void orthogonalize_m3_stable(float R[3][3], int axis, bool normalize);
void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize);
+bool orthogonalize_m3_zero_axes(float R[3][3], const float unit_length);
+bool orthogonalize_m4_zero_axes(float R[4][4], const float unit_length);
+
bool is_orthogonal_m3(const float mat[3][3]);
bool is_orthogonal_m4(const float mat[4][4]);
bool is_orthonormal_m3(const float mat[3][3]);
@@ -359,7 +362,7 @@ void loc_quat_size_to_mat4(float R[4][4],
const float size[3]);
void loc_axisangle_size_to_mat4(float R[4][4],
const float loc[3],
- const float axis[4],
+ const float axis[3],
const float angle,
const float size[3]);
diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h
index 13481e27e2a..39a79efc7e2 100644
--- a/source/blender/blenlib/BLI_math_solvers.h
+++ b/source/blender/blenlib/BLI_math_solvers.h
@@ -41,7 +41,7 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3],
float r_eigen_values[3],
float r_eigen_vectors[3][3]);
-void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]);
+void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3][3]);
/***************************** Simple Solvers ************************************/
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index bb1e1a1c38d..b43f86af670 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -146,7 +146,7 @@ MINLINE void mul_v3_v3(float r[3], const float a[3]);
MINLINE void mul_v3_v3v3(float r[3], const float a[3], const float b[3]);
MINLINE void mul_v4_fl(float r[4], float f);
MINLINE void mul_v4_v4(float r[4], const float a[4]);
-MINLINE void mul_v4_v4fl(float r[3], const float a[4], float f);
+MINLINE void mul_v4_v4fl(float r[4], const float a[4], float f);
MINLINE void mul_v2_v2_cw(float r[2], const float mat[2], const float vec[2]);
MINLINE void mul_v2_v2_ccw(float r[2], const float mat[2], const float vec[2]);
MINLINE float mul_project_m4_v3_zfac(const float mat[4][4],
@@ -177,7 +177,7 @@ MINLINE void negate_v2_v2(float r[2], const float a[2]);
MINLINE void negate_v3(float r[3]);
MINLINE void negate_v3_v3(float r[3], const float a[3]);
MINLINE void negate_v4(float r[4]);
-MINLINE void negate_v4_v4(float r[4], const float a[3]);
+MINLINE void negate_v4_v4(float r[4], const float a[4]);
MINLINE void negate_v3_short(short r[3]);
MINLINE void negate_v3_db(double r[3]);
@@ -323,11 +323,11 @@ void flip_v2_v2v2(float v[2], const float v1[2], const float v2[2]);
/********************************* Comparison ********************************/
-MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool is_zero_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT;
-bool is_finite_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT;
+bool is_finite_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/blenlib/BLI_memarena.h b/source/blender/blenlib/BLI_memarena.h
index 87a320e336d..d7798f12fcc 100644
--- a/source/blender/blenlib/BLI_memarena.h
+++ b/source/blender/blenlib/BLI_memarena.h
@@ -38,7 +38,8 @@ extern "C" {
struct MemArena;
typedef struct MemArena MemArena;
-struct MemArena *BLI_memarena_new(const size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT
+struct MemArena *BLI_memarena_new(const size_t bufsize,
+ const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
ATTR_NONNULL(2) ATTR_MALLOC;
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1);
void BLI_memarena_use_malloc(struct MemArena *ma) ATTR_NONNULL(1);
diff --git a/source/blender/blenlib/BLI_memiter.h b/source/blender/blenlib/BLI_memiter.h
index c7a715309e1..abb1bec809d 100644
--- a/source/blender/blenlib/BLI_memiter.h
+++ b/source/blender/blenlib/BLI_memiter.h
@@ -36,15 +36,14 @@ struct BLI_memiter;
typedef struct BLI_memiter BLI_memiter;
/* warning, ATTR_MALLOC flag on BLI_memiter_alloc causes crash, see: D2756 */
-BLI_memiter *BLI_memiter_create(unsigned int chunk_size) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
-void *BLI_memiter_alloc(BLI_memiter *mi,
- unsigned int size) ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL(1);
+BLI_memiter *BLI_memiter_create(unsigned int chunk_size)
+ ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
+void *BLI_memiter_alloc(BLI_memiter *mi, unsigned int size)
+ ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1);
void BLI_memiter_alloc_from(BLI_memiter *mi, uint elem_size, const void *data_from)
ATTR_NONNULL(1, 3);
-void *BLI_memiter_calloc(BLI_memiter *mi,
- unsigned int size) ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL(1);
+void *BLI_memiter_calloc(BLI_memiter *mi, unsigned int size)
+ ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1);
void BLI_memiter_destroy(BLI_memiter *mi) ATTR_NONNULL(1);
void BLI_memiter_clear(BLI_memiter *mi) ATTR_NONNULL(1);
unsigned int BLI_memiter_count(const BLI_memiter *mi) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
@@ -59,11 +58,11 @@ typedef struct BLI_memiter_handle {
uint elem_left;
} BLI_memiter_handle;
-void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter) ATTR_NONNULL();
-bool BLI_memiter_iter_done(const BLI_memiter_handle *iter) ATTR_NONNULL();
-void *BLI_memiter_iter_step(BLI_memiter_handle *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter) ATTR_NONNULL(1, 2);
+bool BLI_memiter_iter_done(const BLI_memiter_handle *iter) ATTR_NONNULL(1);
+void *BLI_memiter_iter_step(BLI_memiter_handle *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
void *BLI_memiter_iter_step_size(BLI_memiter_handle *iter, uint *r_size) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL();
+ ATTR_NONNULL(1, 2);
#ifdef __cplusplus
}
diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h
index 3ee6f182593..4d9381093c7 100644
--- a/source/blender/blenlib/BLI_mempool.h
+++ b/source/blender/blenlib/BLI_mempool.h
@@ -38,9 +38,12 @@ typedef struct BLI_mempool BLI_mempool;
BLI_mempool *BLI_mempool_create(unsigned int esize,
unsigned int totelem,
unsigned int pchunk,
- unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
-void *BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
-void *BLI_mempool_calloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
+ unsigned int flag)
+ ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
+void *BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
+ ATTR_NONNULL(1);
+void *BLI_mempool_calloc(BLI_mempool *pool)
+ ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1);
void BLI_mempool_free(BLI_mempool *pool, void *addr) ATTR_NONNULL(1, 2);
void BLI_mempool_clear_ex(BLI_mempool *pool, const int totelem_reserve) ATTR_NONNULL(1);
void BLI_mempool_clear(BLI_mempool *pool) ATTR_NONNULL(1);
@@ -68,8 +71,6 @@ typedef struct BLI_mempool_iter {
BLI_mempool *pool;
struct BLI_mempool_chunk *curchunk;
unsigned int curindex;
-
- struct BLI_mempool_chunk **curchunk_threaded_shared;
} BLI_mempool_iter;
/* flag */
@@ -88,11 +89,6 @@ enum {
void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL();
void *BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-BLI_mempool_iter *BLI_mempool_iter_threadsafe_create(BLI_mempool *pool,
- const size_t num_iter) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL();
-void BLI_mempool_iter_threadsafe_free(BLI_mempool_iter *iter_arr) ATTR_NONNULL();
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/BLI_mesh_intersect.hh b/source/blender/blenlib/BLI_mesh_intersect.hh
index a7996939bb1..6b8e79f376c 100644
--- a/source/blender/blenlib/BLI_mesh_intersect.hh
+++ b/source/blender/blenlib/BLI_mesh_intersect.hh
@@ -29,6 +29,7 @@
# include "BLI_array.hh"
# include "BLI_double3.hh"
+# include "BLI_float3.hh"
# include "BLI_index_range.hh"
# include "BLI_map.hh"
# include "BLI_math_mpq.hh"
@@ -340,6 +341,63 @@ class IMesh {
std::ostream &operator<<(std::ostream &os, const IMesh &mesh);
/**
+ * A Bounding Box using floats, and a routine to detect possible
+ * intersection.
+ */
+struct BoundingBox {
+ float3 min{FLT_MAX, FLT_MAX, FLT_MAX};
+ float3 max{-FLT_MAX, -FLT_MAX, -FLT_MAX};
+
+ BoundingBox() = default;
+ BoundingBox(const float3 &min, const float3 &max) : min(min), max(max)
+ {
+ }
+
+ void combine(const float3 &p)
+ {
+ min.x = min_ff(min.x, p.x);
+ min.y = min_ff(min.y, p.y);
+ min.z = min_ff(min.z, p.z);
+ max.x = max_ff(max.x, p.x);
+ max.y = max_ff(max.y, p.y);
+ max.z = max_ff(max.z, p.z);
+ }
+
+ void combine(const double3 &p)
+ {
+ min.x = min_ff(min.x, static_cast<float>(p.x));
+ min.y = min_ff(min.y, static_cast<float>(p.y));
+ min.z = min_ff(min.z, static_cast<float>(p.z));
+ max.x = max_ff(max.x, static_cast<float>(p.x));
+ max.y = max_ff(max.y, static_cast<float>(p.y));
+ max.z = max_ff(max.z, static_cast<float>(p.z));
+ }
+
+ void combine(const BoundingBox &bb)
+ {
+ min.x = min_ff(min.x, bb.min.x);
+ min.y = min_ff(min.y, bb.min.y);
+ min.z = min_ff(min.z, bb.min.z);
+ max.x = max_ff(max.x, bb.max.x);
+ max.y = max_ff(max.y, bb.max.y);
+ max.z = max_ff(max.z, bb.max.z);
+ }
+
+ void expand(float pad)
+ {
+ min.x -= pad;
+ min.y -= pad;
+ min.z -= pad;
+ max.x += pad;
+ max.y += pad;
+ max.z += pad;
+ }
+};
+
+/** Assume bounding boxes have been expanded by a sufficient epsilon. */
+bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b);
+
+/**
* The output will have duplicate vertices merged and degenerate triangles ignored.
* If the input has overlapping co-planar triangles, then there will be
* as many duplicates as there are overlaps in each overlapping triangular region.
@@ -357,6 +415,9 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in,
bool use_self,
IMeshArena *arena);
+/** Return an IMesh that is a triangulation of a mesh with general polygonal faces. */
+IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena);
+
/** This has the side effect of populating verts in the #IMesh. */
void write_obj_mesh(IMesh &m, const std::string &objname);
diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh
index fb5e2b61cdb..b9eda2ad7e1 100644
--- a/source/blender/blenlib/BLI_mpq3.hh
+++ b/source/blender/blenlib/BLI_mpq3.hh
@@ -218,6 +218,15 @@ struct mpq3 {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
+ static mpq_class dot_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer)
+ {
+ buffer = a;
+ buffer *= b;
+ buffer.x += buffer.y;
+ buffer.x += buffer.z;
+ return buffer.x;
+ }
+
static mpq3 cross(const mpq3 &a, const mpq3 &b)
{
return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
@@ -246,6 +255,13 @@ struct mpq3 {
return mpq3::dot(diff, diff);
}
+ static mpq_class distance_squared_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer)
+ {
+ buffer = a;
+ buffer -= b;
+ return mpq3::dot(buffer, buffer);
+ }
+
static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t)
{
mpq_class s = 1 - t;
diff --git a/source/blender/blenlib/BLI_multi_value_map.hh b/source/blender/blenlib/BLI_multi_value_map.hh
index 98b55067a5c..fb52ac78243 100644
--- a/source/blender/blenlib/BLI_multi_value_map.hh
+++ b/source/blender/blenlib/BLI_multi_value_map.hh
@@ -73,6 +73,12 @@ template<typename Key, typename Value> class MultiValueMap {
vector.append(std::forward<ForwardValue>(value));
}
+ void add_non_duplicates(const Key &key, const Value &value)
+ {
+ Vector<Value> &vector = map_.lookup_or_add_default_as(key);
+ vector.append_non_duplicates(value);
+ }
+
/**
* Add all given values to the key.
*/
diff --git a/source/blender/blenlib/BLI_resource_collector.hh b/source/blender/blenlib/BLI_resource_scope.hh
index 70804ceb1f1..e5a698f25f1 100644
--- a/source/blender/blenlib/BLI_resource_collector.hh
+++ b/source/blender/blenlib/BLI_resource_scope.hh
@@ -19,12 +19,24 @@
/** \file
* \ingroup bli
*
- * A ResourceCollector holds an arbitrary set of resources, that will be destructed and/or freed
- * when the ResourceCollector is destructed. This is useful when some object has to take ownership
- * of other objects, but it does not know the type of those other objects.
+ * A `ResourceScope` takes ownership of arbitrary data/resources. Those resources will be
+ * destructed and/or freed when the `ResourceScope` is destructed. Destruction happens in reverse
+ * order. That allows resources do depend on other resources that have been added before.
*
- * Resources owned by the ResourceCollector will be freed in reverse order. That allows resources
- * that are added later to depend on resources that have been added before.
+ * A `ResourceScope` can also be thought of as a dynamic/runtime version of normal scopes in C++
+ * that are surrounded by braces.
+ *
+ * The main purpose of a `ResourceScope` is to allow functions to inject data into the scope of the
+ * caller. Traditionally, that can only be done by returning a value that owns everything it needs.
+ * This is fine until one has to deal with optional ownership. There are many ways to have a type
+ * optionally own something else, all of which are fairly annoying. A `ResourceScope` can be used
+ * to avoid having to deal with optional ownership. If some value would be owned, it can just be
+ * added to the resource scope, otherwise not.
+ *
+ * When a function takes a `ResourceScope` as parameter, it usually means that its return value
+ * will live at least as long as the passed in resources scope. However, it might also live longer.
+ * That can happen when the function returns a reference to statically allocated data or
+ * dynamically allocated data depending on some condition.
*/
#include "BLI_linear_allocator.hh"
@@ -33,7 +45,7 @@
namespace blender {
-class ResourceCollector : NonCopyable, NonMovable {
+class ResourceScope : NonCopyable, NonMovable {
private:
struct ResourceData {
void *data;
@@ -45,9 +57,9 @@ class ResourceCollector : NonCopyable, NonMovable {
Vector<ResourceData> m_resources;
public:
- ResourceCollector() = default;
+ ResourceScope() = default;
- ~ResourceCollector()
+ ~ResourceScope()
{
/* Free in reversed order. */
for (int64_t i = m_resources.size(); i--;) {
@@ -57,45 +69,53 @@ class ResourceCollector : NonCopyable, NonMovable {
}
/**
- * Pass ownership of the resource to the ResourceCollector. It will be destructed and freed when
+ * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when
* the collector is destructed.
*/
- template<typename T> void add(std::unique_ptr<T> resource, const char *name)
+ template<typename T> T *add(std::unique_ptr<T> resource, const char *name)
{
BLI_assert(resource.get() != nullptr);
+ T *ptr = resource.release();
+ if (ptr == nullptr) {
+ return nullptr;
+ }
this->add(
- resource.release(),
+ ptr,
[](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
delete typed_data;
},
name);
+ return ptr;
}
/**
- * Pass ownership of the resource to the ResourceCollector. It will be destructed when the
+ * Pass ownership of the resource to the ResourceScope. It will be destructed when the
* collector is destructed.
*/
- template<typename T> void add(destruct_ptr<T> resource, const char *name)
+ template<typename T> T *add(destruct_ptr<T> resource, const char *name)
{
+ T *ptr = resource.release();
+ if (ptr == nullptr) {
+ return nullptr;
+ }
/* There is no need to keep track of such types. */
if (std::is_trivially_destructible_v<T>) {
- resource.release();
- return;
+ return ptr;
}
- BLI_assert(resource.get() != nullptr);
this->add(
- resource.release(),
+ ptr,
[](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
typed_data->~T();
},
name);
+ return ptr;
}
/**
- * Pass ownership of some resource to the ResourceCollector. The given free function will be
+ * Pass ownership of some resource to the ResourceScope. The given free function will be
* called when the collector is destructed.
*/
void add(void *userdata, void (*free)(void *), const char *name)
@@ -108,7 +128,7 @@ class ResourceCollector : NonCopyable, NonMovable {
}
/**
- * Construct an object with the same value in the ResourceCollector and return a reference to the
+ * Construct an object with the same value in the ResourceScope and return a reference to the
* new value.
*/
template<typename T> T &add_value(T &&value, const char *name)
@@ -126,7 +146,7 @@ class ResourceCollector : NonCopyable, NonMovable {
}
/**
- * Utility method to construct an instance of type T that will be owned by the ResourceCollector.
+ * Utility method to construct an instance of type T that will be owned by the ResourceScope.
*/
template<typename T, typename... Args> T &construct(const char *name, Args &&... args)
{
@@ -137,7 +157,7 @@ class ResourceCollector : NonCopyable, NonMovable {
}
/**
- * Print the names of all the resources that are owned by this ResourceCollector. This can be
+ * Print the names of all the resources that are owned by this ResourceScope. This can be
* useful for debugging.
*/
void print(StringRef name) const
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index fe511793c46..c3876d4eaf8 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -94,7 +94,7 @@ template<typename T> class Span {
using iterator = const T *;
using size_type = int64_t;
- private:
+ protected:
const T *data_ = nullptr;
int64_t size_ = 0;
@@ -477,7 +477,7 @@ template<typename T> class MutableSpan {
using iterator = T *;
using size_type = int64_t;
- private:
+ protected:
T *data_;
int64_t size_;
@@ -662,6 +662,16 @@ template<typename T> class MutableSpan {
}
/**
+ * Return a reference to the first element in the array. This invokes undefined behavior when the
+ * array is empty.
+ */
+ constexpr T &first() const
+ {
+ BLI_assert(size_ > 0);
+ return data_[0];
+ }
+
+ /**
* Returns a reference to the last element. This invokes undefined behavior when the array is
* empty.
*/
diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh
index 19f77078c5b..d66316a95d9 100644
--- a/source/blender/blenlib/BLI_stack.hh
+++ b/source/blender/blenlib/BLI_stack.hh
@@ -232,13 +232,14 @@ class Stack {
{
this->push_as(std::move(value));
}
- template<typename ForwardT> void push_as(ForwardT &&value)
+ /* This is similar to `std::stack::emplace`. */
+ template<typename... ForwardT> void push_as(ForwardT &&... value)
{
if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(1);
}
try {
- new (top_) T(std::forward<ForwardT>(value));
+ new (top_) T(std::forward<ForwardT>(value)...);
top_++;
size_++;
}
diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h
index 096e7818013..5a80680c350 100644
--- a/source/blender/blenlib/BLI_string.h
+++ b/source/blender/blenlib/BLI_string.h
@@ -68,6 +68,11 @@ char *BLI_str_replaceN(const char *__restrict str,
void BLI_str_replace_char(char *string, char src, char dst) ATTR_NONNULL();
+bool BLI_str_replace_table_exact(char *string,
+ const size_t string_len,
+ const char *replace_table[][2],
+ int replace_table_len);
+
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
ATTR_NONNULL(1, 3) ATTR_PRINTF_FORMAT(3, 4);
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h
index 9e61686b37a..83ccfda7e38 100644
--- a/source/blender/blenlib/BLI_task.h
+++ b/source/blender/blenlib/BLI_task.h
@@ -67,17 +67,55 @@ typedef enum TaskPriority {
TASK_PRIORITY_HIGH,
} TaskPriority;
+/**
+ * Task isolation helps avoid unexpected task scheduling decisions that can lead to bugs if wrong
+ * assumptions were made. Typically that happens when doing "nested threading", i.e. one thread
+ * schedules a bunch of main-tasks and those spawn new sub-tasks.
+ *
+ * What can happen is that when a main-task waits for its sub-tasks to complete on other threads,
+ * another main-task is scheduled within the already running main-task. Generally, this is good,
+ * because it leads to better performance. However, sometimes code (often unintentionally) makes
+ * the assumption that at most one main-task runs on a thread at a time.
+ *
+ * The bugs often show themselves in two ways:
+ * - Deadlock, when a main-task holds a mutex while waiting for its sub-tasks to complete.
+ * - Data corruption, when a main-task makes wrong assumptions about a thread-local variable.
+ *
+ * Task isolation can avoid these bugs by making sure that a main-task does not start executing
+ * another main-task while waiting for its sub-tasks. More precisely, a function that runs in an
+ * isolated region is only allowed to run sub-tasks that were spawned in the same isolated region.
+ *
+ * Unfortunately, incorrect use of task isolation can lead to deadlocks itself. This can happen
+ * when threading primitives are used that separate spawning tasks from executing them. The problem
+ * occurs when a task is spawned in one isolated region while the tasks are waited for in another
+ * isolated region. In this setup, the thread that is waiting for the spawned tasks to complete
+ * cannot run the tasks itself. On a single thread, that causes a deadlock already. When there are
+ * multiple threads, another thread will typically run the task and avoid the deadlock. However, if
+ * this situation happens on all threads at the same time, all threads will deadlock. This happened
+ * in T88598.
+ */
+typedef enum TaskIsolation {
+ /* Do not use task isolation. Always use this when tasks are pushed recursively. */
+ TASK_ISOLATION_OFF,
+ /* Run each task in its own isolated region. */
+ TASK_ISOLATION_ON,
+} TaskIsolation;
+
typedef struct TaskPool TaskPool;
typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata);
typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata);
/* Regular task pool that immediately starts executing tasks as soon as they
* are pushed, either on the current or another thread. */
-TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation);
/* Background: always run tasks in a background thread, never immediately
* execute them. For running background jobs. */
-TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create_background(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation);
/* Background Serial: run tasks one after the other in the background,
* without parallelization between the tasks. */
@@ -87,7 +125,9 @@ TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority pr
* as threads can't immediately start working. But it can be used if the data
* structures the threads operate on are not fully initialized until all tasks
* are created. */
-TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create_suspended(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation);
/* No threads: immediately executes tasks on the same thread. For debugging. */
TaskPool *BLI_task_pool_create_no_threads(void *userdata);
@@ -221,11 +261,14 @@ void BLI_task_parallel_listbase(struct ListBase *listbase,
const TaskParallelSettings *settings);
typedef struct MempoolIterData MempoolIterData;
-typedef void (*TaskParallelMempoolFunc)(void *userdata, MempoolIterData *iter);
+
+typedef void (*TaskParallelMempoolFunc)(void *userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict tls);
void BLI_task_parallel_mempool(struct BLI_mempool *mempool,
void *userdata,
TaskParallelMempoolFunc func,
- const bool use_threading);
+ const TaskParallelSettings *settings);
/* TODO(sergey): Think of a better place for this. */
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
@@ -236,6 +279,12 @@ BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *setti
settings->min_iter_per_thread = 0;
}
+BLI_INLINE void BLI_parallel_mempool_settings_defaults(TaskParallelSettings *settings)
+{
+ memset(settings, 0, sizeof(*settings));
+ settings->use_threading = true;
+}
+
/* Don't use this, store any thread specific data in tls->userdata_chunk instead.
* Only here for code to be removed. */
int BLI_task_parallel_thread_id(const TaskParallelTLS *tls);
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 328d623787b..f6f546c90b9 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -161,7 +161,7 @@ class Vector {
}
/**
- * Create a vector from an array ref. The values in the vector are copy constructed.
+ * Create a vector from a span. The values in the vector are copy constructed.
*/
template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
Vector(Span<U> values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator)
@@ -437,13 +437,17 @@ class Vector {
*/
void append(const T &value)
{
- this->ensure_space_for_one();
- this->append_unchecked(value);
+ this->append_as(value);
}
void append(T &&value)
{
+ this->append_as(std::move(value));
+ }
+ /* This is similar to `std::vector::emplace_back`. */
+ template<typename... ForwardValue> void append_as(ForwardValue &&... value)
+ {
this->ensure_space_for_one();
- this->append_unchecked(std::move(value));
+ this->append_unchecked_as(std::forward<ForwardValue>(value)...);
}
/**
@@ -474,10 +478,18 @@ class Vector {
* behavior when not enough capacity has been reserved beforehand. Only use this in performance
* critical code.
*/
- template<typename ForwardT> void append_unchecked(ForwardT &&value)
+ void append_unchecked(const T &value)
+ {
+ this->append_unchecked_as(value);
+ }
+ void append_unchecked(T &&value)
+ {
+ this->append_unchecked_as(std::move(value));
+ }
+ template<typename... ForwardT> void append_unchecked_as(ForwardT &&... value)
{
BLI_assert(end_ < capacity_end_);
- new (end_) T(std::forward<ForwardT>(value));
+ new (end_) T(std::forward<ForwardT>(value)...);
end_++;
UPDATE_VECTOR_SIZE(this);
}
@@ -657,6 +669,21 @@ class Vector {
}
/**
+ * Return a reference to the first element in the vector.
+ * This invokes undefined behavior when the vector is empty.
+ */
+ const T &first() const
+ {
+ BLI_assert(this->size() > 0);
+ return *begin_;
+ }
+ T &first()
+ {
+ BLI_assert(this->size() > 0);
+ return *begin_;
+ }
+
+ /**
* Return how many values are currently stored in the vector.
*/
int64_t size() const
@@ -713,11 +740,12 @@ class Vector {
BLI_assert(index >= 0);
BLI_assert(index < this->size());
T *element_to_remove = begin_ + index;
- if (element_to_remove < end_) {
- *element_to_remove = std::move(*(end_ - 1));
+ T *last_element = end_ - 1;
+ if (element_to_remove < last_element) {
+ *element_to_remove = std::move(*last_element);
}
- end_--;
- end_->~T();
+ end_ = last_element;
+ last_element->~T();
UPDATE_VECTOR_SIZE(this);
}
diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh
index 14b38d564cb..567e4fd8128 100644
--- a/source/blender/blenlib/BLI_vector_set.hh
+++ b/source/blender/blenlib/BLI_vector_set.hh
@@ -398,6 +398,55 @@ class VectorSet {
}
/**
+ * Return the index of the key in the vector. If the key is not in the set, add it and return its
+ * index.
+ */
+ int64_t index_of_or_add(const Key &key)
+ {
+ return this->index_of_or_add_as(key);
+ }
+ int64_t index_of_or_add(Key &&key)
+ {
+ return this->index_of_or_add_as(std::move(key));
+ }
+ template<typename ForwardKey> int64_t index_of_or_add_as(ForwardKey &&key)
+ {
+ return this->index_of_or_add__impl(std::forward<ForwardKey>(key), hash_(key));
+ }
+
+ /**
+ * Returns the key that is stored in the vector set that compares equal to the given key. This
+ * invokes undefined behavior when the key is not in the set.
+ */
+ const Key &lookup_key(const Key &key) const
+ {
+ return this->lookup_key_as(key);
+ }
+ template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const
+ {
+ const Key *key_ptr = this->lookup_key_ptr_as(key);
+ BLI_assert(key_ptr != nullptr);
+ return *key_ptr;
+ }
+
+ /**
+ * Returns a pointer to the key that is stored in the vector set that compares equal to the given
+ * key. If the key is not in the set, null is returned.
+ */
+ const Key *lookup_key_ptr(const Key &key) const
+ {
+ return this->lookup_key_ptr_as(key);
+ }
+ template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const
+ {
+ const int64_t index = this->index_of_try__impl(key, hash_(key));
+ if (index >= 0) {
+ return keys_ + index;
+ }
+ return nullptr;
+ }
+
+ /**
* Get a pointer to the beginning of the array containing all keys.
*/
const Key *data() const
@@ -484,6 +533,14 @@ class VectorSet {
}
/**
+ * Remove all keys from the vector set.
+ */
+ void clear()
+ {
+ this->noexcept_reset();
+ }
+
+ /**
* Get the number of collisions that the probing strategy has to go through to find the key or
* determine that it is not in the set.
*/
@@ -652,6 +709,26 @@ class VectorSet {
VECTOR_SET_SLOT_PROBING_END();
}
+ template<typename ForwardKey>
+ int64_t index_of_or_add__impl(ForwardKey &&key, const uint64_t hash)
+ {
+ this->ensure_can_add();
+
+ VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
+ return slot.index();
+ }
+ if (slot.is_empty()) {
+ const int64_t index = this->size();
+ new (keys_ + index) Key(std::forward<ForwardKey>(key));
+ slot.occupy(index, hash);
+ occupied_and_removed_slots_++;
+ return index;
+ }
+ }
+ VECTOR_SET_SLOT_PROBING_END();
+ }
+
Key pop__impl()
{
BLI_assert(this->size() > 0);
diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh
index f9b0aaa7de6..1c02bce8411 100644
--- a/source/blender/blenlib/BLI_virtual_array.hh
+++ b/source/blender/blenlib/BLI_virtual_array.hh
@@ -37,6 +37,8 @@
* see of the increased compile time and binary size is worth it.
*/
+#include "BLI_array.hh"
+#include "BLI_index_mask.hh"
#include "BLI_span.hh"
namespace blender {
@@ -71,6 +73,11 @@ template<typename T> class VArray {
return size_ == 0;
}
+ IndexRange index_range() const
+ {
+ return IndexRange(size_);
+ }
+
/* Returns true when the virtual array is stored as a span internally. */
bool is_span() const
{
@@ -82,13 +89,13 @@ template<typename T> class VArray {
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
* virtual array is not stored as a span internally. */
- Span<T> get_span() const
+ Span<T> get_internal_span() const
{
BLI_assert(this->is_span());
if (size_ == 0) {
return {};
}
- return this->get_span_impl();
+ return this->get_internal_span_impl();
}
/* Returns true when the virtual array returns the same value for every index. */
@@ -102,20 +109,46 @@ template<typename T> class VArray {
/* Returns the value that is returned for every index. This invokes undefined behavior if the
* virtual array would not return the same value for every index. */
- T get_single() const
+ T get_internal_single() const
{
BLI_assert(this->is_single());
if (size_ == 1) {
return this->get(0);
}
- return this->get_single_impl();
+ return this->get_internal_single_impl();
}
+ /* Get the element at a specific index. Note that this operator cannot be used to assign values
+ * to an index, because the return value is not a reference. */
T operator[](const int64_t index) const
{
return this->get(index);
}
+ /* Copy the entire virtual array into a span. */
+ void materialize(MutableSpan<T> r_span) const
+ {
+ this->materialize(IndexMask(size_), r_span);
+ }
+
+ /* Copy some indices of the virtual array into a span. */
+ void materialize(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_impl(mask, r_span);
+ }
+
+ void materialize_to_uninitialized(MutableSpan<T> r_span) const
+ {
+ this->materialize_to_uninitialized(IndexMask(size_), r_span);
+ }
+
+ void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_to_uninitialized_impl(mask, r_span);
+ }
+
protected:
virtual T get_impl(const int64_t index) const = 0;
@@ -124,7 +157,7 @@ template<typename T> class VArray {
return false;
}
- virtual Span<T> get_span_impl() const
+ virtual Span<T> get_internal_span_impl() const
{
BLI_assert_unreachable();
return {};
@@ -135,56 +168,196 @@ template<typename T> class VArray {
return false;
}
- virtual T get_single_impl() const
+ virtual T get_internal_single_impl() const
{
/* Provide a default implementation, so that subclasses don't have to provide it. This method
* should never be called because `is_single_impl` returns false by default. */
BLI_assert_unreachable();
return T();
}
+
+ virtual void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ T *dst = r_span.data();
+ if (this->is_span()) {
+ const T *src = this->get_internal_span().data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; });
+ }
+ else if (this->is_single()) {
+ const T single = this->get_internal_single();
+ mask.foreach_index([&](const int64_t i) { dst[i] = single; });
+ }
+ else {
+ mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); });
+ }
+ }
+
+ virtual void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ T *dst = r_span.data();
+ if (this->is_span()) {
+ const T *src = this->get_internal_span().data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); });
+ }
+ else if (this->is_single()) {
+ const T single = this->get_internal_single();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); });
+ }
+ else {
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); });
+ }
+ }
+};
+
+/* Similar to VArray, but the elements are mutable. */
+template<typename T> class VMutableArray : public VArray<T> {
+ public:
+ VMutableArray(const int64_t size) : VArray<T>(size)
+ {
+ }
+
+ void set(const int64_t index, T value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->size_);
+ this->set_impl(index, std::move(value));
+ }
+
+ /* Copy the values from the source span to all elements in the virtual array. */
+ void set_all(Span<T> src)
+ {
+ BLI_assert(src.size() == this->size_);
+ this->set_all_impl(src);
+ }
+
+ MutableSpan<T> get_internal_span()
+ {
+ BLI_assert(this->is_span());
+ Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span();
+ return MutableSpan<T>(const_cast<T *>(span.data()), span.size());
+ }
+
+ protected:
+ virtual void set_impl(const int64_t index, T value) = 0;
+
+ virtual void set_all_impl(Span<T> src)
+ {
+ if (this->is_span()) {
+ const MutableSpan<T> span = this->get_internal_span();
+ initialized_copy_n(src.data(), this->size_, span.data());
+ }
+ else {
+ const int64_t size = this->size_;
+ for (int64_t i = 0; i < size; i++) {
+ this->set(i, src[i]);
+ }
+ }
+ }
};
+template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
+template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
+
/**
- * A virtual array implementation for a span. This class is final so that it can be devirtualized
- * by the compiler in some cases (e.g. when #devirtualize_varray is used).
+ * A virtual array implementation for a span. Methods in this class are final so that it can be
+ * devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used).
*/
-template<typename T> class VArrayForSpan final : public VArray<T> {
- private:
- const T *data_;
+template<typename T> class VArray_For_Span : public VArray<T> {
+ protected:
+ const T *data_ = nullptr;
public:
- VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
+ VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
{
}
protected:
- T get_impl(const int64_t index) const override
+ VArray_For_Span(const int64_t size) : VArray<T>(size)
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ return data_[index];
+ }
+
+ bool is_span_impl() const final
+ {
+ return true;
+ }
+
+ Span<T> get_internal_span_impl() const final
+ {
+ return Span<T>(data_, this->size_);
+ }
+};
+
+template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> {
+ protected:
+ T *data_ = nullptr;
+
+ public:
+ VMutableArray_For_MutableSpan(const MutableSpan<T> data)
+ : VMutableArray<T>(data.size()), data_(data.data())
+ {
+ }
+
+ protected:
+ VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size)
+ {
+ }
+
+ T get_impl(const int64_t index) const final
{
return data_[index];
}
+ void set_impl(const int64_t index, T value) final
+ {
+ data_[index] = value;
+ }
+
bool is_span_impl() const override
{
return true;
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
return Span<T>(data_, this->size_);
}
};
/**
+ * A variant of `VArray_For_Span` that owns the underlying data.
+ * The `Container` type has to implement a `size()` and `data()` method.
+ * The `data()` method has to return a pointer to the first element in the continuous array of
+ * elements.
+ */
+template<typename Container, typename T = typename Container::value_type>
+class VArray_For_ArrayContainer : public VArray_For_Span<T> {
+ private:
+ Container container_;
+
+ public:
+ VArray_For_ArrayContainer(Container container)
+ : VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container))
+ {
+ this->data_ = container_.data();
+ }
+};
+
+/**
* A virtual array implementation that returns the same value for every index. This class is final
* so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is
* used).
*/
-template<typename T> class VArrayForSingle final : public VArray<T> {
+template<typename T> class VArray_For_Single final : public VArray<T> {
private:
T value_;
public:
- VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
+ VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
{
}
@@ -199,7 +372,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
return this->size_ == 1;
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
return Span<T>(&value_, 1);
}
@@ -209,13 +382,207 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
return true;
}
- T get_single_impl() const override
+ T get_internal_single_impl() const override
{
return value_;
}
};
/**
+ * In many cases a virtual array is a span internally. In those cases, access to individual could
+ * be much more efficient than calling a virtual method. When the underlying virtual array is not a
+ * span, this class allocates a new array and copies the values over.
+ *
+ * This should be used in those cases:
+ * - All elements in the virtual array are accessed multiple times.
+ * - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit
+ * from faster access.
+ * - An API is called, that does not accept virtual arrays, but only spans.
+ */
+template<typename T> class VArray_Span final : public Span<T> {
+ private:
+ const VArray<T> &varray_;
+ Array<T> owned_data_;
+
+ public:
+ VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray)
+ {
+ this->size_ = varray_.size();
+ if (varray_.is_span()) {
+ this->data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_.~Array();
+ new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
+ varray_.materialize_to_uninitialized(owned_data_);
+ this->data_ = owned_data_.data();
+ }
+ }
+};
+
+/**
+ * Same as VArray_Span, but for a mutable span.
+ * The important thing to note is that when changing this span, the results might not be
+ * immediately reflected in the underlying virtual array (only when the virtual array is a span
+ * internally). The #save method can be used to write all changes to the underlying virtual array,
+ * if necessary.
+ */
+template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
+ private:
+ VMutableArray<T> &varray_;
+ Array<T> owned_data_;
+ bool save_has_been_called_ = false;
+ bool show_not_saved_warning_ = true;
+
+ public:
+ /* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If
+ * not, a new array has to be allocated as a wrapper for the underlying virtual array. */
+ VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true)
+ : MutableSpan<T>(), varray_(varray)
+ {
+ this->size_ = varray_.size();
+ if (varray_.is_span()) {
+ this->data_ = varray_.get_internal_span().data();
+ }
+ else {
+ if (copy_values_to_span) {
+ owned_data_.~Array();
+ new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
+ varray_.materialize_to_uninitialized(owned_data_);
+ }
+ else {
+ owned_data_.reinitialize(varray_.size());
+ }
+ this->data_ = owned_data_.data();
+ }
+ }
+
+ ~VMutableArray_Span()
+ {
+ if (show_not_saved_warning_) {
+ if (!save_has_been_called_) {
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ }
+ }
+ }
+
+ /* Write back all values from a temporary allocated array to the underlying virtual array. */
+ void save()
+ {
+ save_has_been_called_ = true;
+ if (this->data_ != owned_data_.data()) {
+ return;
+ }
+ varray_.set_all(owned_data_);
+ }
+
+ void disable_not_applied_warning()
+ {
+ show_not_saved_warning_ = false;
+ }
+};
+
+/**
+ * This class makes it easy to create a virtual array for an existing function or lambda. The
+ * `GetFunc` should take a single `index` argument and return the value at that index.
+ */
+template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> {
+ private:
+ GetFunc get_func_;
+
+ public:
+ VArray_For_Func(const int64_t size, GetFunc get_func)
+ : VArray<T>(size), get_func_(std::move(get_func))
+ {
+ }
+
+ private:
+ T get_impl(const int64_t index) const override
+ {
+ return get_func_(index);
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ T *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = get_func_(i); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ T *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(get_func_(i)); });
+ }
+};
+
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+class VArray_For_DerivedSpan : public VArray<ElemT> {
+ private:
+ const StructT *data_;
+
+ public:
+ VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data())
+ {
+ }
+
+ private:
+ ElemT get_impl(const int64_t index) const override
+ {
+ return GetFunc(data_[index]);
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); });
+ }
+};
+
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> {
+ private:
+ StructT *data_;
+
+ public:
+ VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
+ : VMutableArray<ElemT>(data.size()), data_(data.data())
+ {
+ }
+
+ private:
+ ElemT get_impl(const int64_t index) const override
+ {
+ return GetFunc(data_[index]);
+ }
+
+ void set_impl(const int64_t index, ElemT value) override
+ {
+ SetFunc(data_[index], std::move(value));
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); });
+ }
+};
+
+/**
* Generate multiple versions of the given function optimized for different virtual arrays.
* One has to be careful with nesting multiple devirtualizations, because that results in an
* exponential number of function instantiations (increasing compile time and binary size).
@@ -229,14 +596,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool
/* Support disabling the devirtualization to simplify benchmarking. */
if (enable) {
if (varray.is_single()) {
- /* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */
- const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()};
+ /* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */
+ const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()};
func(varray_single);
return;
}
if (varray.is_span()) {
- /* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */
- const VArrayForSpan<T> varray_span{varray.get_span()};
+ /* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */
+ const VArray_For_Span<T> varray_span{varray.get_internal_span()};
func(varray_span);
return;
}
@@ -262,26 +629,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1,
const bool is_single1 = varray1.is_single();
const bool is_single2 = varray2.is_single();
if (is_span1 && is_span2) {
- const VArrayForSpan<T1> varray1_span{varray1.get_span()};
- const VArrayForSpan<T2> varray2_span{varray2.get_span()};
+ const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
+ const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
func(varray1_span, varray2_span);
return;
}
if (is_span1 && is_single2) {
- const VArrayForSpan<T1> varray1_span{varray1.get_span()};
- const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
+ const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
+ const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
func(varray1_span, varray2_single);
return;
}
if (is_single1 && is_span2) {
- const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
- const VArrayForSpan<T2> varray2_span{varray2.get_span()};
+ const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
+ const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
func(varray1_single, varray2_span);
return;
}
if (is_single1 && is_single2) {
- const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
- const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
+ const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
+ const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
func(varray1_single, varray2_single);
return;
}
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index f4aef277595..26b9d641d12 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -39,6 +39,7 @@ set(SRC
intern/BLI_args.c
intern/BLI_array.c
intern/BLI_assert.c
+ intern/BLI_color.cc
intern/BLI_dial_2d.c
intern/BLI_dynstr.c
intern/BLI_filelist.c
@@ -144,6 +145,9 @@ set(SRC
intern/winstuff.c
intern/winstuff_dir.c
+ # Private headers.
+ intern/BLI_mempool_private.h
+
# Header as source (included in C files above).
intern/kdtree_impl.h
intern/list_sort_impl.h
@@ -185,6 +189,7 @@ set(SRC
BLI_edgehash.h
BLI_endian_switch.h
BLI_endian_switch_inline.h
+ BLI_enumerable_thread_specific.hh
BLI_expr_pylike_eval.h
BLI_fileops.h
BLI_fileops_types.h
@@ -259,11 +264,12 @@ set(SRC
BLI_rand.h
BLI_rand.hh
BLI_rect.h
- BLI_resource_collector.hh
+ BLI_resource_scope.hh
BLI_scanfill.h
BLI_session_uuid.h
BLI_set.hh
BLI_set_slots.hh
+ BLI_simd.h
BLI_smallhash.h
BLI_sort.h
BLI_sort_utils.h
@@ -388,6 +394,7 @@ if(WITH_GTESTS)
tests/BLI_array_store_test.cc
tests/BLI_array_test.cc
tests/BLI_array_utils_test.cc
+ tests/BLI_color_test.cc
tests/BLI_delaunay_2d_test.cc
tests/BLI_disjoint_set_test.cc
tests/BLI_edgehash_test.cc
diff --git a/source/blender/blenlib/intern/BLI_color.cc b/source/blender/blenlib/intern/BLI_color.cc
new file mode 100644
index 00000000000..6dcef4f4688
--- /dev/null
+++ b/source/blender/blenlib/intern/BLI_color.cc
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_color.hh"
+
+namespace blender {
+
+std::ostream &operator<<(std::ostream &stream, const eAlpha &space)
+{
+ switch (space) {
+ case eAlpha::Straight: {
+ stream << "Straight";
+ break;
+ }
+ case eAlpha::Premultiplied: {
+ stream << "Premultiplied";
+ break;
+ }
+ }
+ return stream;
+}
+
+std::ostream &operator<<(std::ostream &stream, const eSpace &space)
+{
+ switch (space) {
+ case eSpace::Theme: {
+ stream << "Theme";
+ break;
+ }
+ case eSpace::SceneLinear: {
+ stream << "SceneLinear";
+ break;
+ }
+ case eSpace::SceneLinearByteEncoded: {
+ stream << "SceneLinearByteEncoded";
+ break;
+ }
+ }
+ return stream;
+}
+
+} // namespace blender
diff --git a/source/blender/blenlib/intern/BLI_dial_2d.c b/source/blender/blenlib/intern/BLI_dial_2d.c
index 7363233d573..786fdd219a3 100644
--- a/source/blender/blenlib/intern/BLI_dial_2d.c
+++ b/source/blender/blenlib/intern/BLI_dial_2d.c
@@ -78,7 +78,7 @@ float BLI_dial_angle(Dial *dial, const float current_position[2])
cosval = dot_v2v2(current_direction, dial->initial_direction);
sinval = cross_v2v2(current_direction, dial->initial_direction);
- /* clamp to avoid nans in acos */
+ /* Clamp to avoid NAN's in #acos */
angle = atan2f(sinval, cosval);
/* change of sign, we passed the 180 degree threshold. This means we need to add a turn.
diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c
index 75f934c1fb8..d3000ef38e8 100644
--- a/source/blender/blenlib/intern/BLI_mempool.c
+++ b/source/blender/blenlib/intern/BLI_mempool.c
@@ -36,7 +36,8 @@
#include "BLI_utildefines.h"
-#include "BLI_mempool.h" /* own include */
+#include "BLI_mempool.h" /* own include */
+#include "BLI_mempool_private.h" /* own include */
#include "MEM_guardedalloc.h"
@@ -541,8 +542,12 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter)
iter->pool = pool;
iter->curchunk = pool->chunks;
iter->curindex = 0;
+}
- iter->curchunk_threaded_shared = NULL;
+static void mempool_threadsafe_iternew(BLI_mempool *pool, BLI_mempool_threadsafe_iter *ts_iter)
+{
+ BLI_mempool_iternew(pool, &ts_iter->iter);
+ ts_iter->curchunk_threaded_shared = NULL;
}
/**
@@ -558,33 +563,31 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter)
*
* See BLI_task_parallel_mempool implementation for detailed usage example.
*/
-BLI_mempool_iter *BLI_mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t num_iter)
+ParallelMempoolTaskData *mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t num_iter)
{
BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER);
- BLI_mempool_iter *iter_arr = MEM_mallocN(sizeof(*iter_arr) * num_iter, __func__);
+ ParallelMempoolTaskData *iter_arr = MEM_mallocN(sizeof(*iter_arr) * num_iter, __func__);
BLI_mempool_chunk **curchunk_threaded_shared = MEM_mallocN(sizeof(void *), __func__);
- BLI_mempool_iternew(pool, iter_arr);
-
- *curchunk_threaded_shared = iter_arr->curchunk;
- iter_arr->curchunk_threaded_shared = curchunk_threaded_shared;
+ mempool_threadsafe_iternew(pool, &iter_arr->ts_iter);
+ *curchunk_threaded_shared = iter_arr->ts_iter.iter.curchunk;
+ iter_arr->ts_iter.curchunk_threaded_shared = curchunk_threaded_shared;
for (size_t i = 1; i < num_iter; i++) {
- iter_arr[i] = iter_arr[0];
- *curchunk_threaded_shared = iter_arr[i].curchunk = ((*curchunk_threaded_shared) ?
- (*curchunk_threaded_shared)->next :
- NULL);
+ iter_arr[i].ts_iter = iter_arr[0].ts_iter;
+ *curchunk_threaded_shared = iter_arr[i].ts_iter.iter.curchunk =
+ ((*curchunk_threaded_shared) ? (*curchunk_threaded_shared)->next : NULL);
}
return iter_arr;
}
-void BLI_mempool_iter_threadsafe_free(BLI_mempool_iter *iter_arr)
+void mempool_iter_threadsafe_destroy(ParallelMempoolTaskData *iter_arr)
{
- BLI_assert(iter_arr->curchunk_threaded_shared != NULL);
+ BLI_assert(iter_arr->ts_iter.curchunk_threaded_shared != NULL);
- MEM_freeN(iter_arr->curchunk_threaded_shared);
+ MEM_freeN(iter_arr->ts_iter.curchunk_threaded_shared);
MEM_freeN(iter_arr);
}
@@ -605,19 +608,6 @@ static void *bli_mempool_iternext(BLI_mempool_iter *iter)
if (iter->curindex == iter->pool->pchunk) {
iter->curindex = 0;
- if (iter->curchunk_threaded_shared) {
- while (1) {
- iter->curchunk = *iter->curchunk_threaded_shared;
- if (iter->curchunk == NULL) {
- return ret;
- }
- if (atomic_cas_ptr((void **)iter->curchunk_threaded_shared,
- iter->curchunk,
- iter->curchunk->next) == iter->curchunk) {
- break;
- }
- }
- }
iter->curchunk = iter->curchunk->next;
}
@@ -659,19 +649,54 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter)
}
else {
iter->curindex = 0;
- if (iter->curchunk_threaded_shared) {
- for (iter->curchunk = *iter->curchunk_threaded_shared;
- (iter->curchunk != NULL) && (atomic_cas_ptr((void **)iter->curchunk_threaded_shared,
- iter->curchunk,
- iter->curchunk->next) != iter->curchunk);
- iter->curchunk = *iter->curchunk_threaded_shared) {
- /* pass. */
- }
-
- if (UNLIKELY(iter->curchunk == NULL)) {
- return (ret->freeword == FREEWORD) ? NULL : ret;
- }
+ iter->curchunk = iter->curchunk->next;
+ if (UNLIKELY(iter->curchunk == NULL)) {
+ return (ret->freeword == FREEWORD) ? NULL : ret;
}
+ curnode = CHUNK_DATA(iter->curchunk);
+ }
+ } while (ret->freeword == FREEWORD);
+
+ return ret;
+}
+
+/**
+ * A version of #BLI_mempool_iterstep that uses
+ * #BLI_mempool_threadsafe_iter.curchunk_threaded_shared for threaded iteration support.
+ * (threaded section noted in comments).
+ */
+void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *ts_iter)
+{
+ BLI_mempool_iter *iter = &ts_iter->iter;
+ if (UNLIKELY(iter->curchunk == NULL)) {
+ return NULL;
+ }
+
+ const uint esize = iter->pool->esize;
+ BLI_freenode *curnode = POINTER_OFFSET(CHUNK_DATA(iter->curchunk), (esize * iter->curindex));
+ BLI_freenode *ret;
+ do {
+ ret = curnode;
+
+ if (++iter->curindex != iter->pool->pchunk) {
+ curnode = POINTER_OFFSET(curnode, esize);
+ }
+ else {
+ iter->curindex = 0;
+
+ /* Begin unique to the `threadsafe` version of this function. */
+ for (iter->curchunk = *ts_iter->curchunk_threaded_shared;
+ (iter->curchunk != NULL) && (atomic_cas_ptr((void **)ts_iter->curchunk_threaded_shared,
+ iter->curchunk,
+ iter->curchunk->next) != iter->curchunk);
+ iter->curchunk = *ts_iter->curchunk_threaded_shared) {
+ /* pass. */
+ }
+ if (UNLIKELY(iter->curchunk == NULL)) {
+ return (ret->freeword == FREEWORD) ? NULL : ret;
+ }
+ /* End `threadsafe` exception. */
+
iter->curchunk = iter->curchunk->next;
if (UNLIKELY(iter->curchunk == NULL)) {
return (ret->freeword == FREEWORD) ? NULL : ret;
diff --git a/source/blender/blenlib/intern/BLI_mempool_private.h b/source/blender/blenlib/intern/BLI_mempool_private.h
new file mode 100644
index 00000000000..e1c8205c80c
--- /dev/null
+++ b/source/blender/blenlib/intern/BLI_mempool_private.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * Shared logic for #BLI_task_parallel_mempool to create a threaded iterator,
+ * without exposing the these functions publicly.
+ */
+
+#include "BLI_compiler_attrs.h"
+
+#include "BLI_mempool.h"
+#include "BLI_task.h"
+
+typedef struct BLI_mempool_threadsafe_iter {
+ BLI_mempool_iter iter;
+ struct BLI_mempool_chunk **curchunk_threaded_shared;
+} BLI_mempool_threadsafe_iter;
+
+typedef struct ParallelMempoolTaskData {
+ BLI_mempool_threadsafe_iter ts_iter;
+ TaskParallelTLS tls;
+} ParallelMempoolTaskData;
+
+ParallelMempoolTaskData *mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t num_iter)
+ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+void mempool_iter_threadsafe_destroy(ParallelMempoolTaskData *iter_arr) ATTR_NONNULL();
+
+void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *iter);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenlib/intern/array_utils.c b/source/blender/blenlib/intern/array_utils.c
index 57302f1ff81..25261e82cc9 100644
--- a/source/blender/blenlib/intern/array_utils.c
+++ b/source/blender/blenlib/intern/array_utils.c
@@ -337,10 +337,10 @@ bool _bli_array_iter_spiral_square(const void *arr_v,
center[1] < arr_shape[1]);
const char *arr = arr_v;
- const int stride[2] = {arr_shape[1] * (int)elem_size, (int)elem_size};
+ const int stride[2] = {arr_shape[0] * (int)elem_size, (int)elem_size};
/* Test center first. */
- int ofs[2] = {center[0] * stride[0], center[1] * stride[1]};
+ int ofs[2] = {center[0] * stride[1], center[1] * stride[0]};
if (test_fn(arr + ofs[0] + ofs[1], user_data)) {
return true;
}
diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc
index 06a749ab921..9444d1a29cb 100644
--- a/source/blender/blenlib/intern/delaunay_2d.cc
+++ b/source/blender/blenlib/intern/delaunay_2d.cc
@@ -334,9 +334,6 @@ template<typename T> class CDT_state {
T epsilon;
explicit CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon);
- ~CDT_state()
- {
- }
};
template<typename T> CDTArrangement<T>::~CDTArrangement()
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index 106bd5bc793..107c27da6a2 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -171,7 +171,7 @@ size_t BLI_gzip_mem_to_file_at_pos(
z_stream strm;
unsigned char out[CHUNK];
- fseek(file, gz_stream_offset, 0);
+ BLI_fseek(file, gz_stream_offset, 0);
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
@@ -217,7 +217,7 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g
size_t chunk = 256 * 1024;
unsigned char in[CHUNK];
- fseek(file, gz_stream_offset, 0);
+ BLI_fseek(file, gz_stream_offset, 0);
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c
index e755a8e75b5..98da85e3a8c 100644
--- a/source/blender/blenlib/intern/freetypefont.c
+++ b/source/blender/blenlib/intern/freetypefont.c
@@ -135,7 +135,6 @@ static VChar *freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData *
nu->type = CU_BEZIER;
nu->pntsu = onpoints[j];
nu->resolu = 8;
- nu->flag = CU_2D;
nu->flagu = CU_NURB_CYCLIC;
nu->bezt = bezt;
diff --git a/source/blender/blenlib/intern/list_sort_impl.h b/source/blender/blenlib/intern/list_sort_impl.h
index 8f979ba5b0b..680044f9ccb 100644
--- a/source/blender/blenlib/intern/list_sort_impl.h
+++ b/source/blender/blenlib/intern/list_sort_impl.h
@@ -17,13 +17,13 @@
/** \file
* \ingroup bli
*
- * Common implementation of linked-list a non-recursive mergesort.
+ * Common implementation of linked-list a non-recursive merge-sort.
*
- * Originally from Mono's eglib, adapted for portable inclusion.
+ * Originally from Mono's `eglib`, adapted for portable inclusion.
* This file is to be directly included in C-source,
* with defines to control its use.
*
- * This code requires a typedef named `SORT_IMPL_LINKTYPE` for the list node.
+ * This code requires a `typedef` named `SORT_IMPL_LINKTYPE` for the list node.
* It is assumed that the list type is the type of a pointer to a list
* node, and that the node has a field named 'next' that implements to
* the linked list. No additional invariant is maintained
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 6481fac5a14..c1db9ec1a69 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -192,6 +192,13 @@ MINLINE double ratiod(double min, double max, double pos)
return range == 0 ? 0 : ((pos - min) / range);
}
+/* Map a normalized value, i.e. from interval [0, 1] to interval [a, b] */
+MINLINE float scalenorm(float a, float b, float x)
+{
+ BLI_assert(x <= 1 && x >= 0);
+ return (x * (b - a)) + a;
+}
+
/* used for zoom values*/
MINLINE float power_of_2(float val)
{
@@ -507,6 +514,15 @@ MINLINE int max_ii(int a, int b)
return (b < a) ? a : b;
}
+MINLINE uint min_uu(uint a, uint b)
+{
+ return (a < b) ? a : b;
+}
+MINLINE uint max_uu(uint a, uint b)
+{
+ return (b < a) ? a : b;
+}
+
MINLINE float min_fff(float a, float b, float c)
{
return min_ff(min_ff(a, b), c);
diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c
index 8fd2802a547..abcb3139dc7 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -713,3 +713,75 @@ void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, fl
r_table[i * 4 + 3] = 0.0f;
}
}
+
+/* ****************************** wavelength ******************************** */
+/* Wavelength to RGB. */
+
+/* CIE colour matching functions xBar, yBar, and zBar for
+ * wavelengths from 380 through 780 nanometers, every 5
+ * nanometers.
+ * For a wavelength lambda in this range:
+ * cie_colour_match[(lambda - 380) / 5][0] = xBar
+ * cie_colour_match[(lambda - 380) / 5][1] = yBar
+ * cie_colour_match[(lambda - 380) / 5][2] = zBar */
+
+static float cie_colour_match[81][3] = {
+ {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f},
+ {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f},
+ {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f},
+ {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f},
+ {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f},
+ {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f},
+ {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f},
+ {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f},
+ {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f},
+ {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f},
+ {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f},
+ {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f},
+ {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f},
+ {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f},
+ {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f},
+ {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f},
+ {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f},
+ {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f},
+ {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f},
+ {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f},
+ {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f},
+ {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f},
+ {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f},
+ {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f},
+ {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f},
+ {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f},
+ {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}};
+
+static void wavelength_to_xyz(float xyz[3], float lambda_nm)
+{
+ float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */
+ int i = (int)ii;
+
+ if (i < 0 || i >= 80) {
+ xyz[0] = 0.0f;
+ xyz[1] = 0.0f;
+ xyz[2] = 0.0f;
+ }
+ else {
+ ii -= (float)i;
+ const float *c = cie_colour_match[i];
+ xyz[0] = c[0] + ii * (c[3] - c[0]);
+ xyz[1] = c[1] + ii * (c[4] - c[1]);
+ xyz[2] = c[2] + ii * (c[5] - c[2]);
+ }
+}
+
+void wavelength_to_xyz_table(float *r_table, int width)
+{
+ for (int i = 0; i < width; i++) {
+ float temperature = 380 + 400 / (float)width * (float)i;
+
+ float rgb[3];
+ wavelength_to_xyz(rgb, temperature);
+
+ copy_v3_v3(&r_table[i * 4], rgb);
+ r_table[i * 4 + 3] = 0.0f;
+ }
+}
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 01cda6c9e4a..508de506ae8 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -2353,7 +2353,7 @@ bool isect_planes_v3_fn(
for (i_test = 0; i_test < planes_len; i_test++) {
const float *np_test = planes[i_test];
if (((dot_v3v3(np_test, co_test) + np_test[3]) > eps_isect)) {
- /* For low epsilon values the point could intersect it's own plane. */
+ /* For low epsilon values the point could intersect its own plane. */
if (!ELEM(i_test, i, j, k)) {
break;
}
@@ -5393,7 +5393,7 @@ void accumulate_vertex_normals_poly_v3(float **vertnos,
void tangent_from_uv_v3(const float uv1[2],
const float uv2[2],
- const float uv3[3],
+ const float uv3[2],
const float co1[3],
const float co2[3],
const float co3[3],
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index 2ada05d2965..d447da4de64 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -1700,6 +1700,89 @@ void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize)
}
}
+/* -------------------------------------------------------------------- */
+/** \name Orthogonalize Matrix Zeroed Axes
+ *
+ * Set any zeroed axes to an orthogonal vector in relation to the other axes.
+ *
+ * Typically used so matrix inversion can be performed.
+ *
+ * \note If an object has a zero scaled axis, this function can be used to "clean" the matrix
+ * to behave as if the scale on that axis was `unit_length`. So it can be inverted
+ * or used in matrix multiply without creating degenerate matrices, see: T50103
+ * \{ */
+
+/**
+ * \return true if any axis needed to be modified.
+ */
+static bool orthogonalize_m3_zero_axes_impl(float *mat[3], const float unit_length)
+{
+ enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 };
+ int flag = 0;
+ for (int i = 0; i < 3; i++) {
+ flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0;
+ }
+
+ /* Either all or none are zero, either way we can't properly resolve this
+ * since we need to fill invalid axes from valid ones. */
+ if (ELEM(flag, 0, X | Y | Z)) {
+ return false;
+ }
+
+ switch (flag) {
+ case X | Y: {
+ ortho_v3_v3(mat[1], mat[2]);
+ ATTR_FALLTHROUGH;
+ }
+ case X: {
+ cross_v3_v3v3(mat[0], mat[1], mat[2]);
+ break;
+ }
+
+ case Y | Z: {
+ ortho_v3_v3(mat[2], mat[0]);
+ ATTR_FALLTHROUGH;
+ }
+ case Y: {
+ cross_v3_v3v3(mat[1], mat[0], mat[2]);
+ break;
+ }
+
+ case Z | X: {
+ ortho_v3_v3(mat[0], mat[1]);
+ ATTR_FALLTHROUGH;
+ }
+ case Z: {
+ cross_v3_v3v3(mat[2], mat[0], mat[1]);
+ break;
+ }
+ default: {
+ BLI_assert(0); /* Unreachable! */
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (flag & (1 << i)) {
+ if (UNLIKELY(normalize_v3_length(mat[i], unit_length) == 0.0f)) {
+ mat[i][i] = unit_length;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool orthogonalize_m3_zero_axes(float m[3][3], const float unit_length)
+{
+ return orthogonalize_m3_zero_axes_impl((float *[3]){UNPACK3(m)}, unit_length);
+}
+bool orthogonalize_m4_zero_axes(float m[4][4], const float unit_length)
+{
+ return orthogonalize_m3_zero_axes_impl((float *[3]){UNPACK3(m)}, unit_length);
+}
+
+/** \} */
+
bool is_orthogonal_m3(const float m[3][3])
{
int i, j;
@@ -3196,68 +3279,6 @@ void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
* \{ */
/**
- * Return true if invert should be attempted again.
- *
- * \note Takes an array of points to be usable from 3x3 and 4x4 matrices.
- */
-static bool invert_m3_m3_safe_ortho_prepare(float *mat[3])
-{
- enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 };
- int flag = 0;
- for (int i = 0; i < 3; i++) {
- flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0;
- }
-
- /* Either all or none are zero, either way we can't properly resolve this
- * since we need to fill invalid axes from valid ones. */
- if (ELEM(flag, 0, X | Y | Z)) {
- return false;
- }
-
- switch (flag) {
- case X | Y: {
- ortho_v3_v3(mat[1], mat[2]);
- ATTR_FALLTHROUGH;
- }
- case X: {
- cross_v3_v3v3(mat[0], mat[1], mat[2]);
- break;
- }
-
- case Y | Z: {
- ortho_v3_v3(mat[2], mat[0]);
- ATTR_FALLTHROUGH;
- }
- case Y: {
- cross_v3_v3v3(mat[1], mat[0], mat[2]);
- break;
- }
-
- case Z | X: {
- ortho_v3_v3(mat[0], mat[1]);
- ATTR_FALLTHROUGH;
- }
- case Z: {
- cross_v3_v3v3(mat[2], mat[0], mat[1]);
- break;
- }
- default: {
- BLI_assert(0); /* Unreachable! */
- }
- }
-
- for (int i = 0; i < 3; i++) {
- if (flag & (1 << i)) {
- if (UNLIKELY(normalize_v3(mat[i]) == 0.0f)) {
- mat[i][i] = 1.0f;
- }
- }
- }
-
- return true;
-}
-
-/**
* A safe version of invert that uses valid axes, calculating the zero'd axis
* based on the non-zero ones.
*
@@ -3268,8 +3289,7 @@ void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4])
if (UNLIKELY(!invert_m4_m4(Ainv, A))) {
float Atemp[4][4];
copy_m4_m4(Atemp, A);
- if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) &&
- invert_m4_m4(Ainv, Atemp)))) {
+ if (UNLIKELY(!(orthogonalize_m4_zero_axes(Atemp, 1.0f) && invert_m4_m4(Ainv, Atemp)))) {
unit_m4(Ainv);
}
}
@@ -3280,8 +3300,7 @@ void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3])
if (UNLIKELY(!invert_m3_m3(Ainv, A))) {
float Atemp[3][3];
copy_m3_m3(Atemp, A);
- if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) &&
- invert_m3_m3(Ainv, Atemp)))) {
+ if (UNLIKELY(!(orthogonalize_m3_zero_axes(Atemp, 1.0f) && invert_m3_m3(Ainv, Atemp)))) {
unit_m3(Ainv);
}
}
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index a21e0c8f092..fb7b96fde78 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -412,7 +412,7 @@ bool is_finite_v4(const float v[4])
* this would return the angle at the elbow.
*
* note that when v1/v2/v3 represent 3 points along a straight line
- * that the angle returned will be pi (180deg), rather then 0.0
+ * that the angle returned will be pi (180deg), rather than 0.0
*/
float angle_v3v3v3(const float a[3], const float b[3], const float c[3])
{
diff --git a/source/blender/blenlib/intern/memory_utils.c b/source/blender/blenlib/intern/memory_utils.c
index d477c20a242..5ca7b96c136 100644
--- a/source/blender/blenlib/intern/memory_utils.c
+++ b/source/blender/blenlib/intern/memory_utils.c
@@ -31,7 +31,7 @@
#include "BLI_strict_flags.h"
/**
- * Check if memory is zero'd, as with memset(arr, 0, arr_size)
+ * Check if memory is zeroed, as with `memset(arr, 0, arr_size)`.
*/
bool BLI_memory_is_zero(const void *arr, const size_t arr_size)
{
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index bf70b044d0d..c5c830bb4dd 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -38,10 +38,10 @@
# include "BLI_math_mpq.hh"
# include "BLI_mesh_intersect.hh"
# include "BLI_mpq3.hh"
-# include "BLI_polyfill_2d.h"
# include "BLI_set.hh"
# include "BLI_span.hh"
# include "BLI_stack.hh"
+# include "BLI_task.hh"
# include "BLI_vector.hh"
# include "BLI_vector_set.hh"
@@ -49,6 +49,10 @@
# include "BLI_mesh_boolean.hh"
+# ifdef WITH_TBB
+# include "tbb/spin_mutex.h"
+# endif
+
// # define PERFDEBUG
namespace blender::meshintersect {
@@ -145,11 +149,9 @@ class TriMeshTopology : NonCopyable {
* Else return NO_INDEX. */
int other_tri_if_manifold(Edge e, int t) const
{
- if (edge_tri_.contains(e)) {
- auto *p = edge_tri_.lookup(e);
- if (p->size() == 2) {
- return ((*p)[0] == t) ? (*p)[1] : (*p)[0];
- }
+ auto p = edge_tri_.lookup_ptr(e);
+ if (p != nullptr && (*p)->size() == 2) {
+ return ((**p)[0] == t) ? (**p)[1] : (**p)[0];
}
return NO_INDEX;
}
@@ -1404,9 +1406,9 @@ static int find_cell_for_point_near_edge(mpq3 p,
int dummy_index = p_sorted_dummy - sorted_tris.begin();
int prev_tri = (dummy_index == 0) ? sorted_tris[sorted_tris.size() - 1] :
sorted_tris[dummy_index - 1];
- int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] :
- sorted_tris[dummy_index + 1];
if (dbg_level > 0) {
+ int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] :
+ sorted_tris[dummy_index + 1];
std::cout << "prev tri to dummy = " << prev_tri << "; next tri to dummy = " << next_tri
<< "\n";
}
@@ -1691,9 +1693,24 @@ static int find_containing_cell(const Vert *v,
* If the closest point is on an edge, return 0, 1, or 2
* for edges ab, bc, or ca in *r_edge; else -1.
* (Adapted from #closest_on_tri_to_point_v3()).
+ * The arguments ab, ac, ..., r are used as temporaries
+ * in this routine. Passing them in from the caller can
+ * avoid many allocs and frees of temporary mpq3 values
+ * and the mpq_class values within them.
*/
-static mpq_class closest_on_tri_to_point(
- const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert)
+static mpq_class closest_on_tri_to_point(const mpq3 &p,
+ const mpq3 &a,
+ const mpq3 &b,
+ const mpq3 &c,
+ mpq3 &ab,
+ mpq3 &ac,
+ mpq3 &ap,
+ mpq3 &bp,
+ mpq3 &cp,
+ mpq3 &m,
+ mpq3 &r,
+ int *r_edge,
+ int *r_vert)
{
constexpr int dbg_level = 0;
if (dbg_level > 0) {
@@ -1701,11 +1718,15 @@ static mpq_class closest_on_tri_to_point(
std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n";
}
/* Check if p in vertex region outside a. */
- mpq3 ab = b - a;
- mpq3 ac = c - a;
- mpq3 ap = p - a;
- mpq_class d1 = mpq3::dot(ab, ap);
- mpq_class d2 = mpq3::dot(ac, ap);
+ ab = b;
+ ab -= a;
+ ac = c;
+ ac -= a;
+ ap = p;
+ ap -= a;
+
+ mpq_class d1 = mpq3::dot_with_buffer(ab, ap, m);
+ mpq_class d2 = mpq3::dot_with_buffer(ac, ap, m);
if (d1 <= 0 && d2 <= 0) {
/* Barycentric coordinates (1,0,0). */
*r_edge = -1;
@@ -1713,12 +1734,13 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = a\n";
}
- return mpq3::distance_squared(p, a);
+ return mpq3::distance_squared_with_buffer(p, a, m);
}
/* Check if p in vertex region outside b. */
- mpq3 bp = p - b;
- mpq_class d3 = mpq3::dot(ab, bp);
- mpq_class d4 = mpq3::dot(ac, bp);
+ bp = p;
+ bp -= b;
+ mpq_class d3 = mpq3::dot_with_buffer(ab, bp, m);
+ mpq_class d4 = mpq3::dot_with_buffer(ac, bp, m);
if (d3 >= 0 && d4 <= d3) {
/* Barycentric coordinates (0,1,0). */
*r_edge = -1;
@@ -1726,25 +1748,28 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = b\n";
}
- return mpq3::distance_squared(p, b);
+ return mpq3::distance_squared_with_buffer(p, b, m);
}
/* Check if p in region of ab. */
mpq_class vc = d1 * d4 - d3 * d2;
if (vc <= 0 && d1 >= 0 && d3 <= 0) {
mpq_class v = d1 / (d1 - d3);
/* Barycentric coordinates (1-v,v,0). */
- mpq3 r = a + v * ab;
+ r = ab;
+ r *= v;
+ r += a;
*r_vert = -1;
*r_edge = 0;
if (dbg_level > 0) {
std::cout << " answer = on ab at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* Check if p in vertex region outside c. */
- mpq3 cp = p - c;
- mpq_class d5 = mpq3::dot(ab, cp);
- mpq_class d6 = mpq3::dot(ac, cp);
+ cp = p;
+ cp -= c;
+ mpq_class d5 = mpq3::dot_with_buffer(ab, cp, m);
+ mpq_class d6 = mpq3::dot_with_buffer(ac, cp, m);
if (d6 >= 0 && d5 <= d6) {
/* Barycentric coordinates (0,0,1). */
*r_edge = -1;
@@ -1752,49 +1777,67 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = c\n";
}
- return mpq3::distance_squared(p, c);
+ return mpq3::distance_squared_with_buffer(p, c, m);
}
/* Check if p in edge region of ac. */
mpq_class vb = d5 * d2 - d1 * d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
mpq_class w = d2 / (d2 - d6);
/* Barycentric coordinates (1-w,0,w). */
- mpq3 r = a + w * ac;
+ r = ac;
+ r *= w;
+ r += a;
*r_vert = -1;
*r_edge = 2;
if (dbg_level > 0) {
std::cout << " answer = on ac at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* Check if p in edge region of bc. */
mpq_class va = d3 * d6 - d5 * d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
/* Barycentric coordinates (0,1-w,w). */
- mpq3 r = c - b;
- r = w * r;
- r = r + b;
+ r = c;
+ r -= b;
+ r *= w;
+ r += b;
*r_vert = -1;
*r_edge = 1;
if (dbg_level > 0) {
std::cout << " answer = on bc at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* p inside face region. Compute barycentric coordinates (u,v,w). */
mpq_class denom = 1 / (va + vb + vc);
mpq_class v = vb * denom;
mpq_class w = vc * denom;
- ac = w * ac;
- mpq3 r = a + v * ab;
- r = r + ac;
+ ac *= w;
+ r = ab;
+ r *= v;
+ r += a;
+ r += ac;
*r_vert = -1;
*r_edge = -1;
if (dbg_level > 0) {
std::cout << " answer = inside at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
+}
+
+static float closest_on_tri_to_point_float_dist_squared(const float3 &p,
+ const double3 &a,
+ const double3 &b,
+ const double3 &c)
+{
+ float3 fa, fb, fc, closest;
+ copy_v3fl_v3db(fa, a);
+ copy_v3fl_v3db(fb, b);
+ copy_v3fl_v3db(fc, c);
+ closest_on_tri_to_point_v3(closest, p, fa, fb, fc);
+ return len_squared_v3v3(p, closest);
}
struct ComponentContainer {
@@ -1820,6 +1863,7 @@ static Vector<ComponentContainer> find_component_containers(int comp,
const IMesh &tm,
const PatchesInfo &pinfo,
const TriMeshTopology &tmtopo,
+ Array<BoundingBox> &comp_bb,
IMeshArena *arena)
{
constexpr int dbg_level = 0;
@@ -1833,6 +1877,11 @@ static Vector<ComponentContainer> find_component_containers(int comp,
if (dbg_level > 0) {
std::cout << "test vertex in comp: " << test_v << "\n";
}
+ const double3 &test_v_d = test_v->co;
+ float3 test_v_f(test_v_d[0], test_v_d[1], test_v_d[2]);
+
+ mpq3 buf[7];
+
for (int comp_other : components.index_range()) {
if (comp == comp_other) {
continue;
@@ -1840,10 +1889,17 @@ static Vector<ComponentContainer> find_component_containers(int comp,
if (dbg_level > 0) {
std::cout << "comp_other = " << comp_other << "\n";
}
+ if (!bbs_might_intersect(comp_bb[comp], comp_bb[comp_other])) {
+ if (dbg_level > 0) {
+ std::cout << "bounding boxes don't overlap\n";
+ }
+ continue;
+ }
int nearest_tri = NO_INDEX;
int nearest_tri_close_vert = -1;
int nearest_tri_close_edge = -1;
mpq_class nearest_tri_dist_squared;
+ float nearest_tri_dist_squared_float = FLT_MAX;
for (int p : components[comp_other]) {
const Patch &patch = pinfo.patch(p);
for (int t : patch.tris()) {
@@ -1853,10 +1909,23 @@ static Vector<ComponentContainer> find_component_containers(int comp,
}
int close_vert;
int close_edge;
+ /* Try a cheap float test first. */
+ float d2_f = closest_on_tri_to_point_float_dist_squared(
+ test_v_f, tri[0]->co, tri[1]->co, tri[2]->co);
+ if (d2_f - FLT_EPSILON > nearest_tri_dist_squared_float) {
+ continue;
+ }
mpq_class d2 = closest_on_tri_to_point(test_v->co_exact,
tri[0]->co_exact,
tri[1]->co_exact,
tri[2]->co_exact,
+ buf[0],
+ buf[1],
+ buf[2],
+ buf[3],
+ buf[4],
+ buf[5],
+ buf[6],
&close_edge,
&close_vert);
if (dbg_level > 1) {
@@ -1868,6 +1937,7 @@ static Vector<ComponentContainer> find_component_containers(int comp,
nearest_tri_close_edge = close_edge;
nearest_tri_close_vert = close_vert;
nearest_tri_dist_squared = d2;
+ nearest_tri_dist_squared_float = d2_f;
}
}
}
@@ -1899,6 +1969,51 @@ static Vector<ComponentContainer> find_component_containers(int comp,
}
/**
+ * Populate the per-component bounding boxes, expanding them
+ * by an appropriate epsilon so that we conservatively will say
+ * that components could intersect if the BBs overlap.
+ */
+static void populate_comp_bbs(const Vector<Vector<int>> &components,
+ const PatchesInfo &pinfo,
+ const IMesh &im,
+ Array<BoundingBox> &comp_bb)
+{
+ const int comp_grainsize = 16;
+ /* To get a good expansion epsilon, we need to find the maximum
+ * absolute value of any coordinate. Do it first per component,
+ * then get the overall max. */
+ Array<double> max_abs(components.size(), 0.0);
+ parallel_for(components.index_range(), comp_grainsize, [&](IndexRange comp_range) {
+ for (int c : comp_range) {
+ BoundingBox &bb = comp_bb[c];
+ double &maxa = max_abs[c];
+ for (int p : components[c]) {
+ const Patch &patch = pinfo.patch(p);
+ for (int t : patch.tris()) {
+ const Face &tri = *im.face(t);
+ for (const Vert *v : tri) {
+ bb.combine(v->co);
+ for (int i = 0; i < 3; ++i) {
+ maxa = max_dd(maxa, fabs(v->co[i]));
+ }
+ }
+ }
+ }
+ }
+ });
+ double all_max_abs = 0.0;
+ for (int c : components.index_range()) {
+ all_max_abs = max_dd(all_max_abs, max_abs[c]);
+ }
+ constexpr float pad_factor = 10.0f;
+ float pad = all_max_abs == 0.0 ? FLT_EPSILON : 2 * FLT_EPSILON * all_max_abs;
+ pad *= pad_factor;
+ for (int c : components.index_range()) {
+ comp_bb[c].expand(pad);
+ }
+}
+
+/**
* The cells and patches are supposed to form a bipartite graph.
* The graph may be disconnected (if parts of meshes are nested or side-by-side
* without intersection with other each other).
@@ -1940,19 +2055,23 @@ static void finish_patch_cell_graph(const IMesh &tm,
}
int tot_components = components.size();
Array<Vector<ComponentContainer>> comp_cont(tot_components);
- for (int comp : components.index_range()) {
- comp_cont[comp] = find_component_containers(
- comp, components, ambient_cell, tm, pinfo, tmtopo, arena);
- }
- if (dbg_level > 0) {
- std::cout << "component containers:\n";
- for (int comp : comp_cont.index_range()) {
- std::cout << comp << ": ";
- for (const ComponentContainer &cc : comp_cont[comp]) {
- std::cout << "[containing_comp=" << cc.containing_component
- << ", nearest_cell=" << cc.nearest_cell << ", d2=" << cc.dist_to_cell << "] ";
+ if (tot_components > 1) {
+ Array<BoundingBox> comp_bb(tot_components);
+ populate_comp_bbs(components, pinfo, tm, comp_bb);
+ for (int comp : components.index_range()) {
+ comp_cont[comp] = find_component_containers(
+ comp, components, ambient_cell, tm, pinfo, tmtopo, comp_bb, arena);
+ }
+ if (dbg_level > 0) {
+ std::cout << "component containers:\n";
+ for (int comp : comp_cont.index_range()) {
+ std::cout << comp << ": ";
+ for (const ComponentContainer &cc : comp_cont[comp]) {
+ std::cout << "[containing_comp=" << cc.containing_component
+ << ", nearest_cell=" << cc.nearest_cell << ", d2=" << cc.dist_to_cell << "] ";
+ }
+ std::cout << "\n";
}
- std::cout << "\n";
}
}
if (dbg_level > 1) {
@@ -2568,47 +2687,58 @@ static IMesh raycast_tris_boolean(const IMesh &tm,
BVHTree *tree = raycast_tree(tm);
Vector<Face *> out_faces;
out_faces.reserve(tm.face_size());
- Array<float> in_shape(nshapes, 0);
- Array<int> winding(nshapes, 0);
- for (int t : tm.face_index_range()) {
- Face &tri = *tm.face(t);
- int shape = shape_fn(tri.orig);
- if (dbg_level > 0) {
- std::cout << "process triangle " << t << " = " << &tri << "\n";
- std::cout << "shape = " << shape << "\n";
- }
- test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape);
- for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
- if (other_shape == shape) {
- continue;
- }
- /* The in_shape array has a confidence value for "insideness".
- * For most operations, even a hint of being inside
- * gives good results, but when shape is a cutter in a Difference
- * operation, we want to be pretty sure that the point is inside other_shape.
- * E.g., T75827.
- * Also, when the operation is intersection, we also want high confidence.
- */
- bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
- op == BoolOpType::Intersect;
- bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
+# ifdef WITH_TBB
+ tbb::spin_mutex mtx;
+# endif
+ const int grainsize = 256;
+ parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) {
+ Array<float> in_shape(nshapes, 0);
+ Array<int> winding(nshapes, 0);
+ for (int t : range) {
+ Face &tri = *tm.face(t);
+ int shape = shape_fn(tri.orig);
if (dbg_level > 0) {
- std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
- << other_shape << " val = " << in_shape[other_shape] << "\n";
+ std::cout << "process triangle " << t << " = " << &tri << "\n";
+ std::cout << "shape = " << shape << "\n";
}
- winding[other_shape] = inside;
- }
- bool do_flip;
- bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
- if (!do_remove) {
- if (!do_flip) {
- out_faces.append(&tri);
+ test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape);
+ for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
+ if (other_shape == shape) {
+ continue;
+ }
+ /* The in_shape array has a confidence value for "insideness".
+ * For most operations, even a hint of being inside
+ * gives good results, but when shape is a cutter in a Difference
+ * operation, we want to be pretty sure that the point is inside other_shape.
+ * E.g., T75827.
+ * Also, when the operation is intersection, we also want high confidence.
+ */
+ bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
+ op == BoolOpType::Intersect;
+ bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
+ if (dbg_level > 0) {
+ std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
+ << other_shape << " val = " << in_shape[other_shape] << "\n";
+ }
+ winding[other_shape] = inside;
}
- else {
- raycast_add_flipped(out_faces, tri, arena);
+ bool do_flip;
+ bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
+ {
+# ifdef WITH_TBB
+ tbb::spin_mutex::scoped_lock lock(mtx);
+# endif
+ if (!do_remove) {
+ if (!do_flip) {
+ out_faces.append(&tri);
+ }
+ else {
+ raycast_add_flipped(out_faces, tri, arena);
+ }
+ }
}
}
- }
+ });
BLI_bvhtree_free(tree);
ans.set_faces(out_faces);
return ans;
@@ -2680,113 +2810,10 @@ static IMesh raycast_patches_boolean(const IMesh &tm,
}
}
BLI_bvhtree_free(tree);
- ans.set_faces(out_faces);
- return ans;
-}
-
-/**
- * Tessellate face f into triangles and return an array of `const Face *`
- * giving that triangulation. Intended to be used when f has > 4 vertices.
- * Care is taken so that the original edge index associated with
- * each edge in the output triangles either matches the original edge
- * for the (identical) edge of f, or else is -1. So diagonals added
- * for triangulation can later be identified by having #NO_INDEX for original.
- */
-static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
-{
- /* Similar to loop body in BM_mesh_calc_tesselation. */
- int flen = f->size();
- BLI_assert(flen > 4);
- if (!f->plane_populated()) {
- f->populate_plane(false);
- }
- /* Project along negative face normal so (x,y) can be used in 2d. */
- const double3 &poly_normal = f->plane->norm;
- float no[3] = {float(poly_normal[0]), float(poly_normal[1]), float(poly_normal[2])};
- normalize_v3(no);
- float axis_mat[3][3];
- float(*projverts)[2];
- unsigned int(*tris)[3];
- const int totfilltri = flen - 2;
- /* Prepare projected vertices and array to receive triangles in tesselation. */
- tris = static_cast<unsigned int(*)[3]>(MEM_malloc_arrayN(totfilltri, sizeof(*tris), __func__));
- projverts = static_cast<float(*)[2]>(MEM_malloc_arrayN(flen, sizeof(*projverts), __func__));
- axis_dominant_v3_to_m3_negate(axis_mat, no);
- for (int j = 0; j < flen; ++j) {
- const double3 &dco = (*f)[j]->co;
- float co[3] = {float(dco[0]), float(dco[1]), float(dco[2])};
- mul_v2_m3v3(projverts[j], axis_mat, co);
- }
- BLI_polyfill_calc(projverts, flen, 1, tris);
- /* Put tesselation triangles into Face form. Record original edges where they exist. */
- Array<Face *> ans(totfilltri);
- for (int t = 0; t < totfilltri; ++t) {
- unsigned int *tri = tris[t];
- int eo[3];
- const Vert *v[3];
- for (int k = 0; k < 3; k++) {
- BLI_assert(tri[k] < flen);
- v[k] = (*f)[tri[k]];
- /* If tri edge goes between two successive indices in
- * the original face, then it is an original edge. */
- if ((tri[k] + 1) % flen == tri[(k + 1) % 3]) {
- eo[k] = f->edge_orig[tri[k]];
- }
- else {
- eo[k] = NO_INDEX;
- }
- ans[t] = arena->add_face(
- {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
- }
- }
-
- MEM_freeN(tris);
- MEM_freeN(projverts);
+ ans.set_faces(out_faces);
return ans;
}
-
-/**
- * Return an #IMesh that is a triangulation of a mesh with general
- * polygonal faces, #IMesh.
- * Added diagonals will be distinguishable by having edge original
- * indices of #NO_INDEX.
- */
-static IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena)
-{
- Vector<Face *> face_tris;
- constexpr int estimated_tris_per_face = 3;
- face_tris.reserve(estimated_tris_per_face * imesh.face_size());
- for (Face *f : imesh.faces()) {
- /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */
- int flen = f->size();
- if (flen == 3) {
- face_tris.append(f);
- }
- else if (flen == 4) {
- const Vert *v0 = (*f)[0];
- const Vert *v1 = (*f)[1];
- const Vert *v2 = (*f)[2];
- const Vert *v3 = (*f)[3];
- int eo_01 = f->edge_orig[0];
- int eo_12 = f->edge_orig[1];
- int eo_23 = f->edge_orig[2];
- int eo_30 = f->edge_orig[3];
- Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false});
- Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false});
- face_tris.append(f0);
- face_tris.append(f1);
- }
- else {
- Array<Face *> tris = triangulate_poly(f, arena);
- for (Face *tri : tris) {
- face_tris.append(tri);
- }
- }
- }
- return IMesh(face_tris);
-}
-
/**
* If \a tri1 and \a tri2 have a common edge (in opposite orientation),
* return the indices into \a tri1 and \a tri2 where that common edge starts. Else return (-1,-1).
@@ -3363,9 +3390,13 @@ static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out,
std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n";
}
/* For now: need plane normals for all triangles. */
- for (Face *tri : tm_out.faces()) {
- tri->populate_plane(false);
- }
+ const int grainsize = 1024;
+ parallel_for(tm_out.face_index_range(), grainsize, [&](IndexRange range) {
+ for (int i : range) {
+ Face *tri = tm_out.face(i);
+ tri->populate_plane(false);
+ }
+ });
/* Gather all output triangles that are part of each input face.
* face_output_tris[f] will be indices of triangles in tm_out
* that have f as their original face. */
diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc
index b2b8dd4e900..5b7b6d9a929 100644
--- a/source/blender/blenlib/intern/mesh_intersect.cc
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -39,6 +39,7 @@
# include "BLI_math_mpq.hh"
# include "BLI_mpq2.hh"
# include "BLI_mpq3.hh"
+# include "BLI_polyfill_2d.h"
# include "BLI_set.hh"
# include "BLI_span.hh"
# include "BLI_task.h"
@@ -527,9 +528,7 @@ IMeshArena::IMeshArena()
pimpl_ = std::make_unique<IMeshArenaImpl>();
}
-IMeshArena::~IMeshArena()
-{
-}
+IMeshArena::~IMeshArena() = default;
void IMeshArena::reserve(int vert_num_hint, int face_num_hint)
{
@@ -745,83 +744,12 @@ std::ostream &operator<<(std::ostream &os, const IMesh &mesh)
return os;
}
-struct BoundingBox {
- float3 min{FLT_MAX, FLT_MAX, FLT_MAX};
- float3 max{-FLT_MAX, -FLT_MAX, -FLT_MAX};
-
- BoundingBox() = default;
- BoundingBox(const float3 &min, const float3 &max) : min(min), max(max)
- {
- }
- BoundingBox(const BoundingBox &other) : min(other.min), max(other.max)
- {
- }
- BoundingBox(BoundingBox &&other) noexcept : min(std::move(other.min)), max(std::move(other.max))
- {
- }
- ~BoundingBox() = default;
- BoundingBox operator=(const BoundingBox &other)
- {
- if (this != &other) {
- min = other.min;
- max = other.max;
- }
- return *this;
- }
- BoundingBox operator=(BoundingBox &&other) noexcept
- {
- min = std::move(other.min);
- max = std::move(other.max);
- return *this;
- }
-
- void combine(const float3 &p)
- {
- min.x = min_ff(min.x, p.x);
- min.y = min_ff(min.y, p.y);
- min.z = min_ff(min.z, p.z);
- max.x = max_ff(max.x, p.x);
- max.y = max_ff(max.y, p.y);
- max.z = max_ff(max.z, p.z);
- }
-
- void combine(const double3 &p)
- {
- min.x = min_ff(min.x, static_cast<float>(p.x));
- min.y = min_ff(min.y, static_cast<float>(p.y));
- min.z = min_ff(min.z, static_cast<float>(p.z));
- max.x = max_ff(max.x, static_cast<float>(p.x));
- max.y = max_ff(max.y, static_cast<float>(p.y));
- max.z = max_ff(max.z, static_cast<float>(p.z));
- }
-
- void combine(const BoundingBox &bb)
- {
- min.x = min_ff(min.x, bb.min.x);
- min.y = min_ff(min.y, bb.min.y);
- min.z = min_ff(min.z, bb.min.z);
- max.x = max_ff(max.x, bb.max.x);
- max.y = max_ff(max.y, bb.max.y);
- max.z = max_ff(max.z, bb.max.z);
- }
-
- void expand(float pad)
- {
- min.x -= pad;
- min.y -= pad;
- min.z -= pad;
- max.x += pad;
- max.y += pad;
- max.z += pad;
- }
-};
-
/**
* Assume bounding boxes have been expanded by a sufficient epsilon on all sides
* so that the comparisons against the bb bounds are sufficient to guarantee that
* if an overlap or even touching could happen, this will return true.
*/
-static bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b)
+bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b)
{
return isect_aabb_aabb_v3(bb_a.min, bb_a.max, bb_b.min, bb_b.max);
}
@@ -936,28 +864,6 @@ class CoplanarCluster {
{
this->add_tri(t, bb);
}
- CoplanarCluster(const CoplanarCluster &other) : tris_(other.tris_), bb_(other.bb_)
- {
- }
- CoplanarCluster(CoplanarCluster &&other) noexcept
- : tris_(std::move(other.tris_)), bb_(std::move(other.bb_))
- {
- }
- ~CoplanarCluster() = default;
- CoplanarCluster &operator=(const CoplanarCluster &other)
- {
- if (this != &other) {
- tris_ = other.tris_;
- bb_ = other.bb_;
- }
- return *this;
- }
- CoplanarCluster &operator=(CoplanarCluster &&other) noexcept
- {
- tris_ = std::move(other.tris_);
- bb_ = std::move(other.bb_);
- return *this;
- }
/* Assume that caller knows this will not be a duplicate. */
void add_tri(int t, const BoundingBox &bb)
@@ -1073,38 +979,6 @@ struct ITT_value {
ITT_value(ITT_value_kind k, const mpq3 &p1, const mpq3 &p2) : p1(p1), p2(p2), kind(k)
{
}
- ITT_value(const ITT_value &other)
- : p1(other.p1), p2(other.p2), t_source(other.t_source), kind(other.kind)
- {
- }
- ITT_value(ITT_value &&other) noexcept
- : p1(std::move(other.p1)),
- p2(std::move(other.p2)),
- t_source(other.t_source),
- kind(other.kind)
- {
- }
- ~ITT_value()
- {
- }
- ITT_value &operator=(const ITT_value &other)
- {
- if (this != &other) {
- kind = other.kind;
- p1 = other.p1;
- p2 = other.p2;
- t_source = other.t_source;
- }
- return *this;
- }
- ITT_value &operator=(ITT_value &&other) noexcept
- {
- kind = other.kind;
- p1 = std::move(other.p1);
- p2 = std::move(other.p2);
- t_source = other.t_source;
- return *this;
- }
};
static std::ostream &operator<<(std::ostream &os, const ITT_value &itt);
@@ -1241,13 +1115,19 @@ static int filter_plane_side(const double3 &p,
* Assumes ab is not perpendicular to n.
* This works because the ratio of the projections of ab and ac onto n is the same as
* the ratio along the line ab of the intersection point to the whole of ab.
+ * The ab, ac, and dotbuf arguments are used as a temporaries; declaring them
+ * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures.
*/
-static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n)
-{
- mpq3 ab = a - b;
- mpq_class den = mpq3::dot(ab, n);
+static inline mpq3 tti_interp(
+ const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf)
+{
+ ab = a;
+ ab -= b;
+ ac = a;
+ ac -= c;
+ mpq_class den = mpq3::dot_with_buffer(ab, n, dotbuf);
BLI_assert(den != 0);
- mpq_class alpha = mpq3::dot(a - c, n) / den;
+ mpq_class alpha = mpq3::dot_with_buffer(ac, n, dotbuf) / den;
return a - alpha * ab;
}
@@ -1255,11 +1135,28 @@ static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const
* Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW
* order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations.
* TODO: change arguments to `const Vert *` and use floating filters.
+ * The ba, ca, n, and dotbuf arguments are used as temporaries; declaring them
+ * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures.
*/
-static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad)
+static inline int tti_above(const mpq3 &a,
+ const mpq3 &b,
+ const mpq3 &c,
+ const mpq3 &ad,
+ mpq3 &ba,
+ mpq3 &ca,
+ mpq3 &n,
+ mpq3 &dotbuf)
{
- mpq3 n = mpq3::cross(b - a, c - a);
- return sgn(mpq3::dot(ad, n));
+ ba = b;
+ ba -= a;
+ ca = c;
+ ca -= a;
+
+ n.x = ba.y * ca.z - ba.z * ca.y;
+ n.y = ba.z * ca.x - ba.x * ca.z;
+ n.z = ba.x * ca.y - ba.y * ca.x;
+
+ return sgn(mpq3::dot_with_buffer(ad, n, dotbuf));
}
/**
@@ -1303,20 +1200,21 @@ static ITT_value itt_canon2(const mpq3 &p1,
mpq3 p1p2 = p2 - p1;
mpq3 intersect_1;
mpq3 intersect_2;
+ mpq3 buf[4];
bool no_overlap = false;
/* Top test in classification tree. */
- if (tti_above(p1, q1, r2, p1p2) > 0) {
+ if (tti_above(p1, q1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) {
/* Middle right test in classification tree. */
- if (tti_above(p1, r1, r2, p1p2) <= 0) {
+ if (tti_above(p1, r1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) <= 0) {
/* Bottom right test in classification tree. */
- if (tti_above(p1, r1, q2, p1p2) > 0) {
+ if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) {
/* Overlap is [k [i l] j]. */
if (dbg_level > 0) {
std::cout << "overlap [k [i l] j]\n";
}
/* i is intersect with p1r1. l is intersect with p2r2. */
- intersect_1 = tti_interp(p1, r1, p2, n2);
- intersect_2 = tti_interp(p2, r2, p1, n1);
+ intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]);
}
else {
/* Overlap is [i [k l] j]. */
@@ -1324,8 +1222,8 @@ static ITT_value itt_canon2(const mpq3 &p1,
std::cout << "overlap [i [k l] j]\n";
}
/* k is intersect with p2q2. l is intersect is p2r2. */
- intersect_1 = tti_interp(p2, q2, p1, n1);
- intersect_2 = tti_interp(p2, r2, p1, n1);
+ intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]);
}
}
else {
@@ -1338,7 +1236,7 @@ static ITT_value itt_canon2(const mpq3 &p1,
}
else {
/* Middle left test in classification tree. */
- if (tti_above(p1, q1, q2, p1p2) < 0) {
+ if (tti_above(p1, q1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) < 0) {
/* No overlap: [i j] [k l]. */
if (dbg_level > 0) {
std::cout << "no overlap: [i j] [k l]\n";
@@ -1347,14 +1245,14 @@ static ITT_value itt_canon2(const mpq3 &p1,
}
else {
/* Bottom left test in classification tree. */
- if (tti_above(p1, r1, q2, p1p2) >= 0) {
+ if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) >= 0) {
/* Overlap is [k [i j] l]. */
if (dbg_level > 0) {
std::cout << "overlap [k [i j] l]\n";
}
/* i is intersect with p1r1. j is intersect with p1q1. */
- intersect_1 = tti_interp(p1, r1, p2, n2);
- intersect_2 = tti_interp(p1, q1, p2, n2);
+ intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]);
}
else {
/* Overlap is [i [k j] l]. */
@@ -1362,8 +1260,8 @@ static ITT_value itt_canon2(const mpq3 &p1,
std::cout << "overlap [i [k j] l]\n";
}
/* k is intersect with p2q2. j is intersect with p1q1. */
- intersect_1 = tti_interp(p2, q2, p1, n1);
- intersect_2 = tti_interp(p1, q1, p2, n2);
+ intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]);
}
}
}
@@ -1514,6 +1412,7 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
return ITT_value(INONE);
}
+ mpq3 buf[2];
const mpq3 &p1 = vp1->co_exact;
const mpq3 &q1 = vq1->co_exact;
const mpq3 &r1 = vr1->co_exact;
@@ -1523,13 +1422,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
const mpq3 &n2 = tri2.plane->norm_exact;
if (sp1 == 0) {
- sp1 = sgn(mpq3::dot(p1 - r2, n2));
+ buf[0] = p1;
+ buf[0] -= r2;
+ sp1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (sq1 == 0) {
- sq1 = sgn(mpq3::dot(q1 - r2, n2));
+ buf[0] = q1;
+ buf[0] -= r2;
+ sq1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (sr1 == 0) {
- sr1 = sgn(mpq3::dot(r1 - r2, n2));
+ buf[0] = r1;
+ buf[0] -= r2;
+ sr1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (dbg_level > 1) {
@@ -1549,13 +1454,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
/* Repeat for signs of t2's vertices with respect to plane of t1. */
const mpq3 &n1 = tri1.plane->norm_exact;
if (sp2 == 0) {
- sp2 = sgn(mpq3::dot(p2 - r1, n1));
+ buf[0] = p2;
+ buf[0] -= r1;
+ sp2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (sq2 == 0) {
- sq2 = sgn(mpq3::dot(q2 - r1, n1));
+ buf[0] = q2;
+ buf[0] -= r1;
+ sq2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (sr2 == 0) {
- sr2 = sgn(mpq3::dot(r2 - r1, n1));
+ buf[0] = r2;
+ buf[0] -= r1;
+ sr2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (dbg_level > 1) {
@@ -1653,6 +1564,9 @@ struct CDT_data {
Vector<bool> is_reversed;
/** Result of running CDT on input with (vert, edge, face). */
CDT_result<mpq_class> cdt_out;
+ /** To speed up get_cdt_edge_orig, sometimes populate this map from vertex pair to output edge.
+ */
+ Map<std::pair<int, int>, int> verts_to_edge;
int proj_axis;
};
@@ -1811,6 +1725,30 @@ static CDT_data prepare_cdt_input_for_cluster(const IMesh &tm,
return ans;
}
+/* Return a copy of the argument with the integers ordered in ascending order. */
+static inline std::pair<int, int> sorted_int_pair(std::pair<int, int> pair)
+{
+ if (pair.first <= pair.second) {
+ return pair;
+ }
+ return std::pair<int, int>(pair.second, pair.first);
+}
+
+/**
+ * Build cd.verts_to_edge to map from a pair of cdt output indices to an index in cd.cdt_out.edge.
+ * Order the vertex indices so that the smaller one is first in the pair.
+ */
+static void populate_cdt_edge_map(Map<std::pair<int, int>, int> &verts_to_edge,
+ const CDT_result<mpq_class> &cdt_out)
+{
+ verts_to_edge.reserve(cdt_out.edge.size());
+ for (int e : cdt_out.edge.index_range()) {
+ std::pair<int, int> vpair = sorted_int_pair(cdt_out.edge[e]);
+ /* There should be only one edge for each vertex pair. */
+ verts_to_edge.add(vpair, e);
+ }
+}
+
/**
* Fills in cd.cdt_out with result of doing the cdt calculation on (vert, edge, face).
*/
@@ -1843,6 +1781,10 @@ static void do_cdt(CDT_data &cd)
}
cdt_in.epsilon = 0; /* TODO: needs attention for non-exact T. */
cd.cdt_out = blender::meshintersect::delaunay_2d_calc(cdt_in, CDT_INSIDE);
+ constexpr int make_edge_map_threshold = 15;
+ if (cd.cdt_out.edge.size() >= make_edge_map_threshold) {
+ populate_cdt_edge_map(cd.verts_to_edge, cd.cdt_out);
+ }
if (dbg_level > 0) {
std::cout << "\nCDT result\nVerts:\n";
for (int i : cd.cdt_out.vert.index_range()) {
@@ -1879,39 +1821,52 @@ static int get_cdt_edge_orig(
{
int foff = cd.cdt_out.face_edge_offset;
*r_is_intersect = false;
- for (int e : cd.cdt_out.edge.index_range()) {
- std::pair<int, int> edge = cd.cdt_out.edge[e];
- if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) {
- /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */
- /* TODO: if edge has origs from more than on part of the nary input,
- * then want to set *r_is_intersect to true. */
- for (int orig_index : cd.cdt_out.edge_orig[e]) {
- /* orig_index encodes the triangle and pos within the triangle of the input edge. */
- if (orig_index >= foff) {
- int in_face_index = (orig_index / foff) - 1;
- int pos = orig_index % foff;
- /* We need to retrieve the edge orig field from the Face used to populate the
- * in_face_index'th face of the CDT, at the pos'th position of the face. */
- int in_tm_face_index = cd.input_face[in_face_index];
- BLI_assert(in_tm_face_index < in_tm.face_size());
- const Face *facep = in_tm.face(in_tm_face_index);
- BLI_assert(pos < facep->size());
- bool is_rev = cd.is_reversed[in_face_index];
- int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos];
- if (eorig != NO_INDEX) {
- return eorig;
- }
- }
- else {
- /* This edge came from an edge input to the CDT problem,
- * so it is an intersect edge. */
- *r_is_intersect = true;
- /* TODO: maybe there is an orig index:
- * This happens if an input edge was formed by an input face having
- * an edge that is co-planar with the cluster, while the face as a whole is not. */
- return NO_INDEX;
- }
+ int e = NO_INDEX;
+ if (cd.verts_to_edge.size() > 0) {
+ /* Use the populated map to find the edge, if any, between vertices i0 and i1. */
+ std::pair<int, int> vpair = sorted_int_pair(std::pair<int, int>(i0, i1));
+ e = cd.verts_to_edge.lookup_default(vpair, NO_INDEX);
+ }
+ else {
+ for (int ee : cd.cdt_out.edge.index_range()) {
+ std::pair<int, int> edge = cd.cdt_out.edge[ee];
+ if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) {
+ e = ee;
+ break;
}
+ }
+ }
+ if (e == NO_INDEX) {
+ return NO_INDEX;
+ }
+
+ /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */
+ /* TODO: if edge has origs from more than on part of the nary input,
+ * then want to set *r_is_intersect to true. */
+ for (int orig_index : cd.cdt_out.edge_orig[e]) {
+ /* orig_index encodes the triangle and pos within the triangle of the input edge. */
+ if (orig_index >= foff) {
+ int in_face_index = (orig_index / foff) - 1;
+ int pos = orig_index % foff;
+ /* We need to retrieve the edge orig field from the Face used to populate the
+ * in_face_index'th face of the CDT, at the pos'th position of the face. */
+ int in_tm_face_index = cd.input_face[in_face_index];
+ BLI_assert(in_tm_face_index < in_tm.face_size());
+ const Face *facep = in_tm.face(in_tm_face_index);
+ BLI_assert(pos < facep->size());
+ bool is_rev = cd.is_reversed[in_face_index];
+ int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos];
+ if (eorig != NO_INDEX) {
+ return eorig;
+ }
+ }
+ else {
+ /* This edge came from an edge input to the CDT problem,
+ * so it is an intersect edge. */
+ *r_is_intersect = true;
+ /* TODO: maybe there is an orig index:
+ * This happens if an input edge was formed by an input face having
+ * an edge that is co-planar with the cluster, while the face as a whole is not. */
return NO_INDEX;
}
}
@@ -1919,6 +1874,256 @@ static int get_cdt_edge_orig(
}
/**
+ * Make a Face from the CDT output triangle cdt_out_t, which has corresponding input triangle
+ * cdt_in_t. The triangle indices that are in cd.input_face are from IMesh tm.
+ * Return a pointer to the newly constructed Face.
+ */
+static Face *cdt_tri_as_imesh_face(
+ int cdt_out_t, int cdt_in_t, const CDT_data &cd, const IMesh &tm, IMeshArena *arena)
+{
+ const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
+ int t_orig = tm.face(cd.input_face[cdt_in_t])->orig;
+ BLI_assert(cdt_out.face[cdt_out_t].size() == 3);
+ int i0 = cdt_out.face[cdt_out_t][0];
+ int i1 = cdt_out.face[cdt_out_t][1];
+ int i2 = cdt_out.face[cdt_out_t][2];
+ mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]);
+ mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]);
+ mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]);
+ /* No need to provide an original index: if coord matches
+ * an original one, then it will already be in the arena
+ * with the correct orig field. */
+ const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX);
+ const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX);
+ const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX);
+ Face *facep;
+ bool is_isect0;
+ bool is_isect1;
+ bool is_isect2;
+ if (cd.is_reversed[cdt_in_t]) {
+ int oe0 = get_cdt_edge_orig(i0, i2, cd, tm, &is_isect0);
+ int oe1 = get_cdt_edge_orig(i2, i1, cd, tm, &is_isect1);
+ int oe2 = get_cdt_edge_orig(i1, i0, cd, tm, &is_isect2);
+ facep = arena->add_face(
+ {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
+ }
+ else {
+ int oe0 = get_cdt_edge_orig(i0, i1, cd, tm, &is_isect0);
+ int oe1 = get_cdt_edge_orig(i1, i2, cd, tm, &is_isect1);
+ int oe2 = get_cdt_edge_orig(i2, i0, cd, tm, &is_isect2);
+ facep = arena->add_face(
+ {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
+ }
+ facep->populate_plane(false);
+ return facep;
+}
+
+/**
+ * Tessellate face f into triangles and return an array of `const Face *`
+ * giving that triangulation. Intended to be used when f has > 4 vertices.
+ * Care is taken so that the original edge index associated with
+ * each edge in the output triangles either matches the original edge
+ * for the (identical) edge of f, or else is -1. So diagonals added
+ * for triangulation can later be identified by having #NO_INDEX for original.
+ *
+ * This code uses Blenlib's BLI_polyfill_calc to do triangulation, and is therefore quite fast.
+ * Unfortunately, it can product degenerate triangles that mesh_intersect will remove, leaving
+ * the mesh non-PWN.
+ */
+static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena)
+{
+ /* Similar to loop body in BM_mesh_calc_tesselation. */
+ int flen = f->size();
+ BLI_assert(flen > 4);
+ if (!f->plane_populated()) {
+ f->populate_plane(false);
+ }
+ /* Project along negative face normal so (x,y) can be used in 2d. */
+ const double3 &poly_normal = f->plane->norm;
+ float no[3] = {float(poly_normal[0]), float(poly_normal[1]), float(poly_normal[2])};
+ normalize_v3(no);
+ float axis_mat[3][3];
+ float(*projverts)[2];
+ unsigned int(*tris)[3];
+ const int totfilltri = flen - 2;
+ /* Prepare projected vertices and array to receive triangles in tesselation. */
+ tris = static_cast<unsigned int(*)[3]>(MEM_malloc_arrayN(totfilltri, sizeof(*tris), __func__));
+ projverts = static_cast<float(*)[2]>(MEM_malloc_arrayN(flen, sizeof(*projverts), __func__));
+ axis_dominant_v3_to_m3_negate(axis_mat, no);
+ for (int j = 0; j < flen; ++j) {
+ const double3 &dco = (*f)[j]->co;
+ float co[3] = {float(dco[0]), float(dco[1]), float(dco[2])};
+ mul_v2_m3v3(projverts[j], axis_mat, co);
+ }
+ BLI_polyfill_calc(projverts, flen, 1, tris);
+ /* Put tesselation triangles into Face form. Record original edges where they exist. */
+ Array<Face *> ans(totfilltri);
+ for (int t = 0; t < totfilltri; ++t) {
+ unsigned int *tri = tris[t];
+ int eo[3];
+ const Vert *v[3];
+ for (int k = 0; k < 3; k++) {
+ BLI_assert(tri[k] < flen);
+ v[k] = (*f)[tri[k]];
+ /* If tri edge goes between two successive indices in
+ * the original face, then it is an original edge. */
+ if ((tri[k] + 1) % flen == tri[(k + 1) % 3]) {
+ eo[k] = f->edge_orig[tri[k]];
+ }
+ else {
+ eo[k] = NO_INDEX;
+ }
+ ans[t] = arena->add_face(
+ {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
+ }
+ }
+
+ MEM_freeN(tris);
+ MEM_freeN(projverts);
+
+ return ans;
+}
+
+/**
+ * Tessellate face f into triangles and return an array of `const Face *`
+ * giving that triangulation.
+ * Care is taken so that the original edge index associated with
+ * each edge in the output triangles either matches the original edge
+ * for the (identical) edge of f, or else is -1. So diagonals added
+ * for triangulation can later be identified by having #NO_INDEX for original.
+ *
+ * The method used is to use the CDT triangulation. Usually that triangulation
+ * will only use the existing vertices. However, if the face self-intersects
+ * then the CDT triangulation will include the intersection points.
+ * If this happens, we use the polyfill triangulator instead. We don't
+ * use the polyfill triangulator by default because it can create degenerate
+ * triangles (which we can handle but they'll create non-manifold meshes).
+ */
+static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
+{
+ int flen = f->size();
+ CDT_input<mpq_class> cdt_in;
+ cdt_in.vert = Array<mpq2>(flen);
+ cdt_in.face = Array<Vector<int>>(1);
+ cdt_in.face[0].reserve(flen);
+ for (int i : f->index_range()) {
+ cdt_in.face[0].append(i);
+ }
+ /* Project poly along dominant axis of normal to get 2d coords. */
+ if (!f->plane_populated()) {
+ f->populate_plane(false);
+ }
+ const double3 &poly_normal = f->plane->norm;
+ int axis = double3::dominant_axis(poly_normal);
+ /* If project down y axis as opposed to x or z, the orientation
+ * of the polygon will be reversed.
+ * Yet another reversal happens if the poly normal in the dominant
+ * direction is opposite that of the positive dominant axis. */
+ bool rev1 = (axis == 1);
+ bool rev2 = poly_normal[axis] < 0;
+ bool rev = rev1 ^ rev2;
+ for (int i = 0; i < flen; ++i) {
+ int ii = rev ? flen - i - 1 : i;
+ mpq2 &p2d = cdt_in.vert[ii];
+ int k = 0;
+ for (int j = 0; j < 3; ++j) {
+ if (j != axis) {
+ p2d[k++] = (*f)[ii]->co_exact[j];
+ }
+ }
+ }
+ CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE);
+ int n_tris = cdt_out.face.size();
+ Array<Face *> ans(n_tris);
+ for (int t = 0; t < n_tris; ++t) {
+ int i_v_out[3];
+ const Vert *v[3];
+ int eo[3];
+ bool needs_steiner = false;
+ for (int i = 0; i < 3; ++i) {
+ i_v_out[i] = cdt_out.face[t][i];
+ if (cdt_out.vert_orig[i_v_out[i]].size() == 0) {
+ needs_steiner = true;
+ break;
+ }
+ v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]];
+ }
+ if (needs_steiner) {
+ /* Fall back on the polyfill triangulator. */
+ return polyfill_triangulate_poly(f, arena);
+ }
+ Map<std::pair<int, int>, int> verts_to_edge;
+ populate_cdt_edge_map(verts_to_edge, cdt_out);
+ int foff = cdt_out.face_edge_offset;
+ for (int i = 0; i < 3; ++i) {
+ std::pair<int, int> vpair(i_v_out[i], i_v_out[(i + 1) % 3]);
+ std::pair<int, int> vpair_canon = sorted_int_pair(vpair);
+ int e_out = verts_to_edge.lookup_default(vpair_canon, NO_INDEX);
+ BLI_assert(e_out != NO_INDEX);
+ eo[i] = NO_INDEX;
+ for (int orig : cdt_out.edge_orig[e_out]) {
+ if (orig >= foff) {
+ int pos = orig % foff;
+ BLI_assert(pos < f->size());
+ eo[i] = f->edge_orig[pos];
+ break;
+ }
+ }
+ }
+ if (rev) {
+ ans[t] = arena->add_face(
+ {v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false});
+ }
+ else {
+ ans[t] = arena->add_face(
+ {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
+ }
+ }
+ return ans;
+}
+
+/**
+ * Return an #IMesh that is a triangulation of a mesh with general
+ * polygonal faces, #IMesh.
+ * Added diagonals will be distinguishable by having edge original
+ * indices of #NO_INDEX.
+ */
+IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena)
+{
+ Vector<Face *> face_tris;
+ constexpr int estimated_tris_per_face = 3;
+ face_tris.reserve(estimated_tris_per_face * imesh.face_size());
+ for (Face *f : imesh.faces()) {
+ /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */
+ int flen = f->size();
+ if (flen == 3) {
+ face_tris.append(f);
+ }
+ else if (flen == 4) {
+ const Vert *v0 = (*f)[0];
+ const Vert *v1 = (*f)[1];
+ const Vert *v2 = (*f)[2];
+ const Vert *v3 = (*f)[3];
+ int eo_01 = f->edge_orig[0];
+ int eo_12 = f->edge_orig[1];
+ int eo_23 = f->edge_orig[2];
+ int eo_30 = f->edge_orig[3];
+ Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false});
+ Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false});
+ face_tris.append(f0);
+ face_tris.append(f1);
+ }
+ else {
+ Array<Face *> tris = triangulate_poly(f, arena);
+ for (Face *tri : tris) {
+ face_tris.append(tri);
+ }
+ }
+ }
+ return IMesh(face_tris);
+}
+
+/**
* Using the result of CDT in cd.cdt_out, extract an #IMesh representing the subdivision
* of input triangle t, which should be an element of cd.input_face.
*/
@@ -1939,55 +2144,17 @@ static IMesh extract_subdivided_tri(const CDT_data &cd,
BLI_assert(false);
return IMesh();
}
- int t_orig = in_tm.face(t)->orig;
constexpr int inline_buf_size = 20;
Vector<Face *, inline_buf_size> faces;
for (int f : cdt_out.face.index_range()) {
if (cdt_out.face_orig[f].contains(t_in_cdt)) {
- BLI_assert(cdt_out.face[f].size() == 3);
- int i0 = cdt_out.face[f][0];
- int i1 = cdt_out.face[f][1];
- int i2 = cdt_out.face[f][2];
- mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]);
- mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]);
- mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]);
- /* No need to provide an original index: if coord matches
- * an original one, then it will already be in the arena
- * with the correct orig field. */
- const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX);
- const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX);
- const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX);
- Face *facep;
- bool is_isect0;
- bool is_isect1;
- bool is_isect2;
- if (cd.is_reversed[t_in_cdt]) {
- int oe0 = get_cdt_edge_orig(i0, i2, cd, in_tm, &is_isect0);
- int oe1 = get_cdt_edge_orig(i2, i1, cd, in_tm, &is_isect1);
- int oe2 = get_cdt_edge_orig(i1, i0, cd, in_tm, &is_isect2);
- facep = arena->add_face(
- {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
- }
- else {
- int oe0 = get_cdt_edge_orig(i0, i1, cd, in_tm, &is_isect0);
- int oe1 = get_cdt_edge_orig(i1, i2, cd, in_tm, &is_isect1);
- int oe2 = get_cdt_edge_orig(i2, i0, cd, in_tm, &is_isect2);
- facep = arena->add_face(
- {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
- }
- facep->populate_plane(false);
+ Face *facep = cdt_tri_as_imesh_face(f, t_in_cdt, cd, in_tm, arena);
faces.append(facep);
}
}
return IMesh(faces);
}
-static IMesh extract_single_tri(const IMesh &tm, int t)
-{
- Face *f = tm.face(t);
- return IMesh({f});
-}
-
static bool bvhtreeverlap_cmp(const BVHTreeOverlap &a, const BVHTreeOverlap &b)
{
if (a.indexA < b.indexA) {
@@ -2203,7 +2370,7 @@ static void calc_overlap_itts(Map<std::pair<int, int>, ITT_value> &itt_map,
}
/**
- * Data needed for parallelization of calc_subdivided_tris.
+ * Data needed for parallelization of calc_subdivided_non_cluster_tris.
*/
struct OverlapTriRange {
int tri_index;
@@ -2280,14 +2447,13 @@ static void calc_subdivided_tri_range_func(void *__restrict userdata,
* r_tri_subdivided with the result of intersecting it with
* all the other triangles in the mesh, if it intersects any others.
* But don't do this for triangles that are part of a cluster.
- * Also, do nothing here if the answer is just the triangle itself.
*/
-static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided,
- const IMesh &tm,
- const Map<std::pair<int, int>, ITT_value> &itt_map,
- const CoplanarClusterInfo &clinfo,
- const TriOverlaps &ov,
- IMeshArena *arena)
+static void calc_subdivided_non_cluster_tris(Array<IMesh> &r_tri_subdivided,
+ const IMesh &tm,
+ const Map<std::pair<int, int>, ITT_value> &itt_map,
+ const CoplanarClusterInfo &clinfo,
+ const TriOverlaps &ov,
+ IMeshArena *arena)
{
const int dbg_level = 0;
if (dbg_level > 0) {
@@ -2327,6 +2493,54 @@ static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided,
settings.use_threading = intersect_use_threading;
BLI_task_parallel_range(
0, overlap_tri_range_tot, &data, calc_subdivided_tri_range_func, &settings);
+ /* Now have to put in the triangles that are the same as the input ones, and not in clusters.
+ */
+ for (int t : tm.face_index_range()) {
+ if (r_tri_subdivided[t].face_size() == 0 && clinfo.tri_cluster(t) == NO_INDEX) {
+ r_tri_subdivided[t] = IMesh({tm.face(t)});
+ }
+ }
+}
+
+/**
+ * For each cluster in clinfo, extract the triangles from the cluster
+ * that correspond to each original triangle t that is part of the cluster,
+ * and put the resulting triangles into an IMesh in tri_subdivided[t].
+ * We have already done the CDT for the triangles in the cluster, whose
+ * result is in cluster_subdivided[c] for each cluster c.
+ */
+static void calc_cluster_tris(Array<IMesh> &tri_subdivided,
+ const IMesh &tm,
+ const CoplanarClusterInfo &clinfo,
+ const Array<CDT_data> &cluster_subdivided,
+ IMeshArena *arena)
+{
+ for (int c : clinfo.index_range()) {
+ const CoplanarCluster &cl = clinfo.cluster(c);
+ const CDT_data &cd = cluster_subdivided[c];
+ /* Each triangle in cluster c should be an input triangle in cd.input_faces.
+ * (See prepare_cdt_input_for_cluster.)
+ * So accumulate a Vector of Face* for each input face by going through the
+ * output faces and making a Face for each input face that it is part of.
+ * (The Boolean algorithm wants duplicates if a given output triangle is part
+ * of more than one input triangle.)
+ */
+ int n_cluster_tris = cl.tot_tri();
+ const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
+ BLI_assert(cd.input_face.size() == n_cluster_tris);
+ Array<Vector<Face *>> face_vec(n_cluster_tris);
+ for (int cdt_out_t : cdt_out.face.index_range()) {
+ for (int cdt_in_t : cdt_out.face_orig[cdt_out_t]) {
+ Face *f = cdt_tri_as_imesh_face(cdt_out_t, cdt_in_t, cd, tm, arena);
+ face_vec[cdt_in_t].append(f);
+ }
+ }
+ for (int cdt_in_t : cd.input_face.index_range()) {
+ int tm_t = cd.input_face[cdt_in_t];
+ BLI_assert(tri_subdivided[tm_t].face_size() == 0);
+ tri_subdivided[tm_t] = IMesh(face_vec[cdt_in_t]);
+ }
+ }
}
static CDT_data calc_cluster_subdivided(const CoplanarClusterInfo &clinfo,
@@ -2699,10 +2913,11 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in,
doperfmax(2, tri_ov.overlap().size());
# endif
Array<IMesh> tri_subdivided(tm_clean->face_size());
- calc_subdivided_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena);
+ calc_subdivided_non_cluster_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena);
# ifdef PERFDEBUG
double subdivided_tris_time = PIL_check_seconds_timer();
- std::cout << "subdivided tris found, time = " << subdivided_tris_time - itt_time << "\n";
+ std::cout << "subdivided non-cluster tris found, time = " << subdivided_tris_time - itt_time
+ << "\n";
# endif
Array<CDT_data> cluster_subdivided(clinfo.tot_cluster());
for (int c : clinfo.index_range()) {
@@ -2713,19 +2928,11 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in,
std::cout << "subdivided clusters found, time = "
<< cluster_subdivide_time - subdivided_tris_time << "\n";
# endif
- for (int t : tm_clean->face_index_range()) {
- int c = clinfo.tri_cluster(t);
- if (c != NO_INDEX) {
- BLI_assert(tri_subdivided[t].face_size() == 0);
- tri_subdivided[t] = extract_subdivided_tri(cluster_subdivided[c], *tm_clean, t, arena);
- }
- else if (tri_subdivided[t].face_size() == 0) {
- tri_subdivided[t] = extract_single_tri(*tm_clean, t);
- }
- }
+ calc_cluster_tris(tri_subdivided, *tm_clean, clinfo, cluster_subdivided, arena);
# ifdef PERFDEBUG
double extract_time = PIL_check_seconds_timer();
- std::cout << "triangles extracted, time = " << extract_time - cluster_subdivide_time << "\n";
+ std::cout << "subdivided cluster tris found, time = " << extract_time - cluster_subdivide_time
+ << "\n";
# endif
IMesh combined = union_tri_subdivides(tri_subdivided);
if (dbg_level > 1) {
diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c
index 996a1622239..8e28088c9fa 100644
--- a/source/blender/blenlib/intern/noise.c
+++ b/source/blender/blenlib/intern/noise.c
@@ -1121,7 +1121,7 @@ static float voronoi_CrS(float x, float y, float z)
/** \name Cell-Noise Implementation
* \{ */
-/* returns unsigned cellnoise */
+/** Returns unsigned cell-noise. */
static float BLI_cellNoiseU(float x, float y, float z)
{
/* avoid precision issues on unit coordinates */
@@ -1166,7 +1166,9 @@ void BLI_noise_cell_v3(float x, float y, float z, float ca[3])
/** \name Public API's
* \{ */
-/* newnoise: generic noise function for use with different noisebases */
+/**
+ * newnoise: generic noise function for use with different `noisebasis`.
+ */
float BLI_noise_generic_noise(
float noisesize, float x, float y, float z, bool hard, int noisebasis)
{
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index b076d0f09c1..e8027836ed6 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -1044,17 +1044,17 @@ bool BLI_path_abs(char *path, const char *basepath)
#else
BLI_strncpy(tmp, path, sizeof(tmp));
- /* Check for loading a windows path on a posix system
- * in this case, there is no use in trying C:/ since it
- * will never exist on a unix os.
+ /* Check for loading a MS-Windows path on a POSIX system
+ * in this case, there is no use in trying `C:/` since it
+ * will never exist on a Unix system.
*
- * Add a '/' prefix and lowercase the drive-letter, remove the ':'.
- * C:\foo.JPG -> /c/foo.JPG */
+ * Add a `/` prefix and lowercase the drive-letter, remove the `:`.
+ * `C:\foo.JPG` -> `/c/foo.JPG` */
if (isalpha(tmp[0]) && (tmp[1] == ':') && ELEM(tmp[2], '\\', '/')) {
tmp[1] = tolower(tmp[0]); /* Replace ':' with drive-letter. */
tmp[0] = '/';
- /* '\' the slash will be converted later */
+ /* `\` the slash will be converted later. */
}
#endif
@@ -2013,9 +2013,9 @@ void BLI_path_slash_native(char *path)
{
#ifdef WIN32
if (path && BLI_strnlen(path, 3) > 2) {
- BLI_str_replace_char(path + 2, '/', '\\');
+ BLI_str_replace_char(path + 2, ALTSEP, SEP);
}
#else
- BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), '\\', '/');
+ BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP);
#endif
}
diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c
index 7bfca149ffb..98fa5c872b0 100644
--- a/source/blender/blenlib/intern/polyfill_2d_beautify.c
+++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c
@@ -375,7 +375,7 @@ void BLI_polyfill_beautify(const float (*coords)[2],
for (uint i = 0, base_index = 0; i < order_edges_len; base_index++) {
const struct OrderEdge *oe_a = &order_edges[i++];
const struct OrderEdge *oe_b = &order_edges[i++];
- BLI_assert(oe_a->verts[0] == oe_a->verts[0] && oe_a->verts[1] == oe_a->verts[1]);
+ BLI_assert(oe_a->verts[0] == oe_b->verts[0] && oe_a->verts[1] == oe_b->verts[1]);
half_edges[oe_a->e_half].e_radial = oe_b->e_half;
half_edges[oe_b->e_half].e_radial = oe_a->e_half;
half_edges[oe_a->e_half].base_index = base_index;
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index 287334a34ee..8964dac31a9 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -266,7 +266,8 @@ eFileAttributes BLI_file_attributes(const char *path)
if (attr & FILE_ATTRIBUTE_SPARSE_FILE) {
ret |= FILE_ATTR_SPARSE_FILE;
}
- if (attr & FILE_ATTRIBUTE_OFFLINE) {
+ if (attr & FILE_ATTRIBUTE_OFFLINE || attr & FILE_ATTRIBUTE_RECALL_ON_OPEN ||
+ attr & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS) {
ret |= FILE_ATTR_OFFLINE;
}
if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
@@ -292,19 +293,23 @@ bool BLI_file_alias_target(const char *filepath,
/* This parameter can only be `const` on Linux since
* redirections are not supported there.
* NOLINTNEXTLINE: readability-non-const-parameter. */
- char r_targetpath[FILE_MAXDIR])
+ char r_targetpath[/*FILE_MAXDIR*/])
{
# ifdef WIN32
if (!BLI_path_extension_check(filepath, ".lnk")) {
return false;
}
- IShellLinkW *Shortcut = NULL;
- bool success = false;
- CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ if (FAILED(hr)) {
+ return false;
+ }
- HRESULT hr = CoCreateInstance(
+ IShellLinkW *Shortcut = NULL;
+ hr = CoCreateInstance(
&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID *)&Shortcut);
+
+ bool success = false;
if (SUCCEEDED(hr)) {
IPersistFile *PersistFile;
hr = Shortcut->lpVtbl->QueryInterface(Shortcut, &IID_IPersistFile, (LPVOID *)&PersistFile);
@@ -328,6 +333,7 @@ bool BLI_file_alias_target(const char *filepath,
Shortcut->lpVtbl->Release(Shortcut);
}
+ CoUninitialize();
return (success && r_targetpath[0]);
# else
UNUSED_VARS(r_targetpath, filepath);
diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c
index ccc11af9f2b..3d40c6ef146 100644
--- a/source/blender/blenlib/intern/string.c
+++ b/source/blender/blenlib/intern/string.c
@@ -540,6 +540,29 @@ void BLI_str_replace_char(char *str, char src, char dst)
}
/**
+ * Simple exact-match string replacement.
+ *
+ * \param replace_table: Array of source, destination pairs.
+ *
+ * \note Larger tables should use a hash table.
+ */
+bool BLI_str_replace_table_exact(char *string,
+ const size_t string_len,
+ const char *replace_table[][2],
+ int replace_table_len)
+{
+ for (int i = 0; i < replace_table_len; i++) {
+ if (STREQ(string, replace_table[i][0])) {
+ BLI_strncpy(string, replace_table[i][1], string_len);
+ return true;
+ }
+ }
+ return false;
+}
+
+/** \} */
+
+/**
* Compare two strings without regard to case.
*
* \retval True if the strings are equal, false otherwise.
diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c
index 38271e5823f..ee41c277b34 100644
--- a/source/blender/blenlib/intern/task_iterator.c
+++ b/source/blender/blenlib/intern/task_iterator.c
@@ -29,11 +29,16 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_mempool.h"
+#include "BLI_mempool_private.h"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "atomic_ops.h"
+/* -------------------------------------------------------------------- */
+/** \name Macros
+ * \{ */
+
/* Allows to avoid using malloc for userdata_chunk in tasks, when small enough. */
#define MALLOCA(_size) ((_size) <= 8192) ? alloca((_size)) : MEM_mallocN((_size), __func__)
#define MALLOCA_FREE(_mem, _size) \
@@ -42,6 +47,12 @@
} \
((void)0)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic Iteration
+ * \{ */
+
BLI_INLINE void task_parallel_calc_chunk_size(const TaskParallelSettings *settings,
const int tot_items,
int num_tasks,
@@ -182,9 +193,12 @@ static void task_parallel_iterator_no_threads(const TaskParallelSettings *settin
parallel_iterator_func_do(state, userdata_chunk);
- if (use_userdata_chunk && settings->func_free != NULL) {
- /* `func_free` should only free data that was created during execution of `func`. */
- settings->func_free(state->userdata, userdata_chunk_local);
+ if (use_userdata_chunk) {
+ if (settings->func_free != NULL) {
+ /* `func_free` should only free data that was created during execution of `func`. */
+ settings->func_free(state->userdata, userdata_chunk_local);
+ }
+ MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size);
}
}
@@ -223,7 +237,7 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings,
void *userdata_chunk_array = NULL;
const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
- TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH);
+ TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
if (use_userdata_chunk) {
userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks);
@@ -241,14 +255,16 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings,
BLI_task_pool_work_and_wait(task_pool);
BLI_task_pool_free(task_pool);
- if (use_userdata_chunk && (settings->func_reduce != NULL || settings->func_free != NULL)) {
- for (size_t i = 0; i < num_tasks; i++) {
- userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
- if (settings->func_reduce != NULL) {
- settings->func_reduce(state->userdata, userdata_chunk, userdata_chunk_local);
- }
- if (settings->func_free != NULL) {
- settings->func_free(state->userdata, userdata_chunk_local);
+ if (use_userdata_chunk) {
+ if (settings->func_reduce != NULL || settings->func_free != NULL) {
+ for (size_t i = 0; i < num_tasks; i++) {
+ userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
+ if (settings->func_reduce != NULL) {
+ settings->func_reduce(state->userdata, userdata_chunk, userdata_chunk_local);
+ }
+ if (settings->func_free != NULL) {
+ settings->func_free(state->userdata, userdata_chunk_local);
+ }
}
}
MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks);
@@ -294,6 +310,12 @@ void BLI_task_parallel_iterator(void *userdata,
task_parallel_iterator_do(settings, &state);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ListBase Iteration
+ * \{ */
+
static void task_parallel_listbase_get(void *__restrict UNUSED(userdata),
const TaskParallelTLS *__restrict UNUSED(tls),
void **r_next_item,
@@ -343,8 +365,11 @@ void BLI_task_parallel_listbase(ListBase *listbase,
task_parallel_iterator_do(settings, &state);
}
-#undef MALLOCA
-#undef MALLOCA_FREE
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name MemPool Iteration
+ * \{ */
typedef struct ParallelMempoolState {
void *userdata;
@@ -354,11 +379,12 @@ typedef struct ParallelMempoolState {
static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata)
{
ParallelMempoolState *__restrict state = BLI_task_pool_user_data(pool);
- BLI_mempool_iter *iter = taskdata;
- MempoolIterData *item;
+ BLI_mempool_threadsafe_iter *iter = &((ParallelMempoolTaskData *)taskdata)->ts_iter;
+ TaskParallelTLS *tls = &((ParallelMempoolTaskData *)taskdata)->tls;
- while ((item = BLI_mempool_iterstep(iter)) != NULL) {
- state->func(state->userdata, item);
+ MempoolIterData *item;
+ while ((item = mempool_iter_threadsafe_step(iter)) != NULL) {
+ state->func(state->userdata, item, tls);
}
}
@@ -368,16 +394,14 @@ static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata)
* \param mempool: The iterable BLI_mempool to loop over.
* \param userdata: Common userdata passed to all instances of \a func.
* \param func: Callback function.
- * \param use_threading: If \a true, actually split-execute loop in threads,
- * else just do a sequential for loop
- * (allows caller to use any kind of test to switch on parallelization or not).
+ * \param settings: See public API doc of TaskParallelSettings for description of all settings.
*
* \note There is no static scheduling here.
*/
void BLI_task_parallel_mempool(BLI_mempool *mempool,
void *userdata,
TaskParallelMempoolFunc func,
- const bool use_threading)
+ const TaskParallelSettings *settings)
{
TaskPool *task_pool;
ParallelMempoolState state;
@@ -387,18 +411,38 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
return;
}
- if (!use_threading) {
+ void *userdata_chunk = settings->userdata_chunk;
+ const size_t userdata_chunk_size = settings->userdata_chunk_size;
+ void *userdata_chunk_local = NULL;
+ void *userdata_chunk_array = NULL;
+ const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
+
+ if (!settings->use_threading) {
+ TaskParallelTLS tls = {NULL};
+ if (use_userdata_chunk) {
+ userdata_chunk_local = MALLOCA(userdata_chunk_size);
+ memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
+ tls.userdata_chunk = userdata_chunk_local;
+ }
+
BLI_mempool_iter iter;
BLI_mempool_iternew(mempool, &iter);
- for (void *item = BLI_mempool_iterstep(&iter); item != NULL;
- item = BLI_mempool_iterstep(&iter)) {
- func(userdata, item);
+ void *item;
+ while ((item = BLI_mempool_iterstep(&iter))) {
+ func(userdata, item, &tls);
+ }
+
+ if (settings->func_free != NULL) {
+ /* `func_free` should only free data that was created during execution of `func`. */
+ settings->func_free(userdata, userdata_chunk_local);
}
+
+ MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size);
return;
}
- task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH);
+ task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
num_threads = BLI_task_scheduler_num_threads();
/* The idea here is to prevent creating task for each of the loop iterations
@@ -410,16 +454,46 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
state.userdata = userdata;
state.func = func;
- BLI_mempool_iter *mempool_iterators = BLI_mempool_iter_threadsafe_create(mempool,
- (size_t)num_tasks);
+ if (use_userdata_chunk) {
+ userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks);
+ }
+
+ ParallelMempoolTaskData *mempool_iterator_data = mempool_iter_threadsafe_create(
+ mempool, (size_t)num_tasks);
for (i = 0; i < num_tasks; i++) {
+ if (use_userdata_chunk) {
+ userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
+ memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
+ }
+ mempool_iterator_data[i].tls.userdata_chunk = userdata_chunk_local;
+
/* Use this pool's pre-allocated tasks. */
- BLI_task_pool_push(task_pool, parallel_mempool_func, &mempool_iterators[i], false, NULL);
+ BLI_task_pool_push(task_pool, parallel_mempool_func, &mempool_iterator_data[i], false, NULL);
}
BLI_task_pool_work_and_wait(task_pool);
BLI_task_pool_free(task_pool);
- BLI_mempool_iter_threadsafe_free(mempool_iterators);
+ if (use_userdata_chunk) {
+ if ((settings->func_free != NULL) || (settings->func_reduce != NULL)) {
+ for (i = 0; i < num_tasks; i++) {
+ if (settings->func_reduce) {
+ settings->func_reduce(
+ userdata, userdata_chunk, mempool_iterator_data[i].tls.userdata_chunk);
+ }
+ if (settings->func_free) {
+ settings->func_free(userdata, mempool_iterator_data[i].tls.userdata_chunk);
+ }
+ }
+ }
+ MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks);
+ }
+
+ mempool_iter_threadsafe_destroy(mempool_iterator_data);
}
+
+#undef MALLOCA
+#undef MALLOCA_FREE
+
+/** \} */
diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc
index 00ba659a9c8..d72674c1c00 100644
--- a/source/blender/blenlib/intern/task_pool.cc
+++ b/source/blender/blenlib/intern/task_pool.cc
@@ -22,6 +22,7 @@
#include <cstdlib>
#include <memory>
+#include <thread>
#include <utility>
#include "MEM_guardedalloc.h"
@@ -111,15 +112,7 @@ class Task {
Task &operator=(const Task &other) = delete;
Task &operator=(Task &&other) = delete;
- /* Execute task. */
- void operator()() const
- {
-#ifdef WITH_TBB
- tbb::this_task_arena::isolate([this] { run(pool, taskdata); });
-#else
- run(pool, taskdata);
-#endif
- }
+ void operator()() const;
};
/* TBB Task Group.
@@ -147,10 +140,6 @@ class TBBTaskGroup : public tbb::task_group {
}
# endif
}
-
- ~TBBTaskGroup()
- {
- }
};
#endif
@@ -167,13 +156,16 @@ enum TaskPoolType {
struct TaskPool {
TaskPoolType type;
bool use_threads;
+ TaskIsolation task_isolation;
ThreadMutex user_mutex;
void *userdata;
- /* TBB task pool. */
#ifdef WITH_TBB
+ /* TBB task pool. */
TBBTaskGroup tbb_group;
+ /* This is used to detect a common way to accidentally create a deadlock with task isolation. */
+ std::thread::id task_pool_create_thread_id;
#endif
volatile bool is_suspended;
BLI_mempool *suspended_mempool;
@@ -184,6 +176,36 @@ struct TaskPool {
volatile bool background_is_canceling;
};
+/* Execute task. */
+void Task::operator()() const
+{
+#ifdef WITH_TBB
+ if (pool->task_isolation == TASK_ISOLATION_ON) {
+ tbb::this_task_arena::isolate([this] { run(pool, taskdata); });
+ return;
+ }
+#endif
+ run(pool, taskdata);
+}
+
+static void assert_on_valid_thread(TaskPool *pool)
+{
+ /* TODO: Remove this `return` to enable the check. */
+ return;
+#ifdef DEBUG
+# ifdef WITH_TBB
+ if (pool->task_isolation == TASK_ISOLATION_ON) {
+ const std::thread::id current_id = std::this_thread::get_id();
+ /* This task pool is modified from different threads. To avoid deadlocks, `TASK_ISOLATION_OFF`
+ * has to be used. Task isolation can still be used in a more fine-grained way within the
+ * tasks, but should not be enabled for the entire task pool. */
+ BLI_assert(pool->task_pool_create_thread_id == current_id);
+ }
+# endif
+#endif
+ UNUSED_VARS_NDEBUG(pool);
+}
+
/* TBB Task Pool.
*
* Task pool using the TBB scheduler for tasks. When building without TBB
@@ -369,7 +391,10 @@ static void background_task_pool_free(TaskPool *pool)
/* Task Pool */
-static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPriority priority)
+static TaskPool *task_pool_create_ex(void *userdata,
+ TaskPoolType type,
+ TaskPriority priority,
+ TaskIsolation task_isolation)
{
const bool use_threads = BLI_task_scheduler_num_threads() > 1 && type != TASK_POOL_NO_THREADS;
@@ -385,6 +410,11 @@ static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPrio
pool->type = type;
pool->use_threads = use_threads;
+ pool->task_isolation = task_isolation;
+
+#ifdef WITH_TBB
+ pool->task_pool_create_thread_id = std::this_thread::get_id();
+#endif
pool->userdata = userdata;
BLI_mutex_init(&pool->user_mutex);
@@ -407,9 +437,9 @@ static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPrio
/**
* Create a normal task pool. Tasks will be executed as soon as they are added.
*/
-TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority)
+TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority, TaskIsolation task_isolation)
{
- return task_pool_create_ex(userdata, TASK_POOL_TBB, priority);
+ return task_pool_create_ex(userdata, TASK_POOL_TBB, priority, task_isolation);
}
/**
@@ -424,9 +454,11 @@ TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority)
* they could end never being executed, since the 'fallback' background thread is already
* busy with parent task in single-threaded context).
*/
-TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority)
+TaskPool *BLI_task_pool_create_background(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation)
{
- return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority);
+ return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority, task_isolation);
}
/**
@@ -434,9 +466,11 @@ TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority)
* for until BLI_task_pool_work_and_wait() is called. This helps reducing threading
* overhead when pushing huge amount of small initial tasks from the main thread.
*/
-TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority)
+TaskPool *BLI_task_pool_create_suspended(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation)
{
- return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority);
+ return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority, task_isolation);
}
/**
@@ -445,7 +479,8 @@ TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority)
*/
TaskPool *BLI_task_pool_create_no_threads(void *userdata)
{
- return task_pool_create_ex(userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH);
+ return task_pool_create_ex(
+ userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
/**
@@ -454,7 +489,7 @@ TaskPool *BLI_task_pool_create_no_threads(void *userdata)
*/
TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority)
{
- return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority);
+ return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority, TASK_ISOLATION_ON);
}
void BLI_task_pool_free(TaskPool *pool)
@@ -482,6 +517,8 @@ void BLI_task_pool_push(TaskPool *pool,
bool free_taskdata,
TaskFreeFunction freedata)
{
+ assert_on_valid_thread(pool);
+
Task task(pool, run, taskdata, free_taskdata, freedata);
switch (pool->type) {
@@ -499,6 +536,8 @@ void BLI_task_pool_push(TaskPool *pool,
void BLI_task_pool_work_and_wait(TaskPool *pool)
{
+ assert_on_valid_thread(pool);
+
switch (pool->type) {
case TASK_POOL_TBB:
case TASK_POOL_TBB_SUSPENDED:
diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c
index 9586da941a4..7d7436411ac 100644
--- a/source/blender/blenlib/intern/timecode.c
+++ b/source/blender/blenlib/intern/timecode.c
@@ -216,10 +216,10 @@ size_t BLI_timecode_string_from_time_simple(char *str,
const int hun = ((int)(fmod(time_seconds, 1.0) * 100));
if (hr) {
- rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
+ rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
}
else {
- rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun);
+ rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun);
}
return rlen;
diff --git a/source/blender/blenlib/intern/uvproject.c b/source/blender/blenlib/intern/uvproject.c
index 329c4d48fe8..093d08e643d 100644
--- a/source/blender/blenlib/intern/uvproject.c
+++ b/source/blender/blenlib/intern/uvproject.c
@@ -134,7 +134,7 @@ void BLI_uvproject_from_view(float target[2],
/* 'rotmat' can be `obedit->obmat` when uv project is used.
* 'winx' and 'winy' can be from `scene->r.xsch/ysch` */
-ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float (*rotmat)[4], float winx, float winy)
+ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float rotmat[4][4], float winx, float winy)
{
ProjCameraInfo uci;
Camera *camera = ob->data;
diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c
index 333b6783087..3aa61d1fec5 100644
--- a/source/blender/blenlib/intern/winstuff.c
+++ b/source/blender/blenlib/intern/winstuff.c
@@ -94,9 +94,9 @@ void BLI_windows_register_blend_extension(const bool background)
GetModuleFileName(0, BlPath, MAX_PATH);
/* Replace the actual app name with the wrapper. */
- blender_app = strstr(BlPath, "blender-app.exe");
+ blender_app = strstr(BlPath, "blender.exe");
if (blender_app != NULL) {
- strcpy(blender_app, "blender.exe");
+ strcpy(blender_app, "blender-launcher.exe");
}
/* root is HKLM by default */
diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc
new file mode 100644
index 00000000000..14796e6bf71
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_color_test.cc
@@ -0,0 +1,133 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_color.hh"
+
+namespace blender::tests {
+
+/**
+ * \name Conversions
+ * \{ */
+
+TEST(color, ThemeByteToFloat)
+{
+ ColorTheme4b theme_byte(192, 128, 64, 128);
+ ColorTheme4f theme_float = theme_byte.to_4f();
+ EXPECT_NEAR(0.75f, theme_float.r, 0.01f);
+ EXPECT_NEAR(0.5f, theme_float.g, 0.01f);
+ EXPECT_NEAR(0.25f, theme_float.b, 0.01f);
+ EXPECT_NEAR(0.5f, theme_float.a, 0.01f);
+}
+
+TEST(color, SrgbStraightFloatToByte)
+{
+ ColorTheme4f theme_float(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme_byte = theme_float.to_4b();
+ EXPECT_EQ(191, theme_byte.r);
+ EXPECT_EQ(128, theme_byte.g);
+ EXPECT_EQ(64, theme_byte.b);
+ EXPECT_EQ(128, theme_byte.a);
+}
+
+TEST(color, SrgbStraightToSceneLinearPremultiplied)
+{
+ BLI_init_srgb_conversion();
+
+ ColorTheme4b theme(192, 128, 64, 128);
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear =
+ BLI_color_convert_to_scene_linear(theme).premultiply_alpha();
+ EXPECT_NEAR(0.26f, linear.r, 0.01f);
+ EXPECT_NEAR(0.11f, linear.g, 0.01f);
+ EXPECT_NEAR(0.02f, linear.b, 0.01f);
+ EXPECT_NEAR(0.5f, linear.a, 0.01f);
+}
+
+TEST(color, SceneLinearStraightToPremultiplied)
+{
+ ColorSceneLinear4f<eAlpha::Straight> straight(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied = straight.premultiply_alpha();
+ EXPECT_NEAR(0.37f, premultiplied.r, 0.01f);
+ EXPECT_NEAR(0.25f, premultiplied.g, 0.01f);
+ EXPECT_NEAR(0.12f, premultiplied.b, 0.01f);
+ EXPECT_NEAR(0.5f, premultiplied.a, 0.01f);
+}
+
+TEST(color, SceneLinearPremultipliedToStraight)
+{
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinear4f<eAlpha::Straight> straight = premultiplied.unpremultiply_alpha();
+ EXPECT_NEAR(1.5f, straight.r, 0.01f);
+ EXPECT_NEAR(1.0f, straight.g, 0.01f);
+ EXPECT_NEAR(0.5f, straight.b, 0.01f);
+ EXPECT_NEAR(0.5f, straight.a, 0.01f);
+}
+
+TEST(color, SceneLinearStraightSrgbFloat)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4f theme = BLI_color_convert_to_theme4f(linear);
+ EXPECT_NEAR(0.88f, theme.r, 0.01);
+ EXPECT_NEAR(0.73f, theme.g, 0.01);
+ EXPECT_NEAR(0.53f, theme.b, 0.01);
+ EXPECT_NEAR(0.5f, theme.a, 0.01);
+}
+
+TEST(color, SceneLinearPremultipliedToSrgbFloat)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4f theme = BLI_color_convert_to_theme4f(linear.unpremultiply_alpha());
+
+ EXPECT_NEAR(1.19f, theme.r, 0.01);
+ EXPECT_NEAR(1.0f, theme.g, 0.01);
+ EXPECT_NEAR(0.74f, theme.b, 0.01);
+ EXPECT_NEAR(0.5f, theme.a, 0.01);
+}
+
+TEST(color, SceneLinearStraightSrgbByte)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme = BLI_color_convert_to_theme4b(linear);
+ EXPECT_EQ(225, theme.r);
+ EXPECT_EQ(188, theme.g);
+ EXPECT_EQ(137, theme.b);
+ EXPECT_EQ(128, theme.a);
+}
+
+TEST(color, SceneLinearPremultipliedToSrgbByte)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme = BLI_color_convert_to_theme4b(linear.unpremultiply_alpha());
+ EXPECT_EQ(255, theme.r);
+ EXPECT_EQ(255, theme.g);
+ EXPECT_EQ(188, theme.b);
+ EXPECT_EQ(128, theme.a);
+}
+
+TEST(color, SceneLinearByteEncoding)
+{
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded = linear.encode();
+ EXPECT_EQ(225, encoded.r);
+ EXPECT_EQ(188, encoded.g);
+ EXPECT_EQ(137, encoded.b);
+ EXPECT_EQ(128, encoded.a);
+}
+
+TEST(color, SceneLinearByteDecoding)
+{
+ ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded(225, 188, 137, 128);
+ ColorSceneLinear4f<eAlpha::Premultiplied> decoded = encoded.decode();
+ EXPECT_NEAR(0.75f, decoded.r, 0.01f);
+ EXPECT_NEAR(0.5f, decoded.g, 0.01f);
+ EXPECT_NEAR(0.25f, decoded.b, 0.01f);
+ EXPECT_NEAR(0.5f, decoded.a, 0.01f);
+}
+
+/* \} */
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_function_ref_test.cc b/source/blender/blenlib/tests/BLI_function_ref_test.cc
index cdcbccc72e8..74f5014142c 100644
--- a/source/blender/blenlib/tests/BLI_function_ref_test.cc
+++ b/source/blender/blenlib/tests/BLI_function_ref_test.cc
@@ -99,4 +99,29 @@ TEST(function_ref, ReferenceAnotherFunctionRef)
EXPECT_EQ(y(), 2);
}
+TEST(function_ref, CallSafe)
+{
+ FunctionRef<int()> f;
+ EXPECT_FALSE(f.call_safe().has_value());
+ auto func = []() { return 10; };
+ f = func;
+ EXPECT_TRUE(f.call_safe().has_value());
+ EXPECT_EQ(*f.call_safe(), 10);
+ f = {};
+ EXPECT_FALSE(f.call_safe().has_value());
+ BLI_STATIC_ASSERT((std::is_same_v<decltype(f.call_safe()), std::optional<int>>), "");
+}
+
+TEST(function_ref, CallSafeVoid)
+{
+ FunctionRef<void()> f;
+ BLI_STATIC_ASSERT((std::is_same_v<decltype(f.call_safe()), void>), "");
+ f.call_safe();
+ int value = 0;
+ auto func = [&]() { value++; };
+ f = func;
+ f.call_safe();
+ EXPECT_EQ(value, 1);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
index 977e5dba497..0e0145e592a 100644
--- a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
+++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
@@ -136,4 +136,17 @@ TEST(linear_allocator, ManyAllocations)
}
}
+TEST(linear_allocator, ConstructArray)
+{
+ LinearAllocator<> allocator;
+ MutableSpan<std::string> strings = allocator.construct_array<std::string>(4, "hello");
+ EXPECT_EQ(strings[0], "hello");
+ EXPECT_EQ(strings[1], "hello");
+ EXPECT_EQ(strings[2], "hello");
+ EXPECT_EQ(strings[3], "hello");
+ for (std::string &string : strings) {
+ string.~basic_string();
+ }
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc b/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc
index e9810aed179..8be89d66062 100644
--- a/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc
+++ b/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc
@@ -81,7 +81,7 @@ TEST(LockfreeLinkList, InsertMultipleConcurrent)
LockfreeLinkList list;
BLI_linklist_lockfree_init(&list);
/* Initialize task scheduler and pool. */
- TaskPool *pool = BLI_task_pool_create_suspended(&list, TASK_PRIORITY_HIGH);
+ TaskPool *pool = BLI_task_pool_create_suspended(&list, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
/* Push tasks to the pool. */
for (int i = 0; i < num_nodes; ++i) {
BLI_task_pool_push(pool, concurrent_insert, POINTER_FROM_INT(i), false, nullptr);
diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc
index f1ae8fb3921..679a10e9ce0 100644
--- a/source/blender/blenlib/tests/BLI_map_test.cc
+++ b/source/blender/blenlib/tests/BLI_map_test.cc
@@ -604,6 +604,55 @@ TEST(map, GenericAlgorithms)
EXPECT_EQ(std::count(map.keys().begin(), map.keys().end(), 7), 1);
}
+TEST(map, AddAsVariadic)
+{
+ Map<int, StringRef> map;
+ map.add_as(3, "hello", 2);
+ map.add_as(2, "test", 1);
+ EXPECT_EQ(map.lookup(3), "he");
+ EXPECT_EQ(map.lookup(2), "t");
+}
+
+TEST(map, RemoveDuringIteration)
+{
+ Map<int, int> map;
+ map.add(2, 1);
+ map.add(5, 2);
+ map.add(1, 2);
+ map.add(6, 0);
+ map.add(3, 3);
+
+ EXPECT_EQ(map.size(), 5);
+
+ using Iter = Map<int, int>::MutableItemIterator;
+ Iter begin = map.items().begin();
+ Iter end = map.items().end();
+ for (Iter iter = begin; iter != end; ++iter) {
+ Map<int, int>::MutableItem item = *iter;
+ if (item.value == 2) {
+ map.remove(iter);
+ }
+ }
+
+ EXPECT_EQ(map.size(), 3);
+ EXPECT_EQ(map.lookup(2), 1);
+ EXPECT_EQ(map.lookup(6), 0);
+ EXPECT_EQ(map.lookup(3), 3);
+}
+
+TEST(map, LookupKey)
+{
+ Map<std::string, int> map;
+ map.add("a", 0);
+ map.add("b", 1);
+ map.add("c", 2);
+ EXPECT_EQ(map.lookup_key("a"), "a");
+ EXPECT_EQ(map.lookup_key_as("c"), "c");
+ EXPECT_EQ(map.lookup_key_ptr_as("d"), nullptr);
+ EXPECT_EQ(map.lookup_key_ptr_as("b")->size(), 1);
+ EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a"));
+}
+
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/
diff --git a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
index f1fcdae3a52..b3108381d78 100644
--- a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
+++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
@@ -93,6 +93,15 @@ TEST(stack, Push)
EXPECT_EQ(stack.size(), 2);
}
+TEST(stack, PushAs)
+{
+ Stack<StringRef> stack;
+ stack.push_as("hello", 3);
+ stack.push_as("world", 1);
+ EXPECT_EQ(stack.pop(), "w");
+ EXPECT_EQ(stack.pop(), "hel");
+}
+
TEST(stack, PushMultiple)
{
Stack<int> stack;
diff --git a/source/blender/blenlib/tests/BLI_task_test.cc b/source/blender/blenlib/tests/BLI_task_test.cc
index fce3e56d105..52603428031 100644
--- a/source/blender/blenlib/tests/BLI_task_test.cc
+++ b/source/blender/blenlib/tests/BLI_task_test.cc
@@ -67,7 +67,9 @@ TEST(task, RangeIter)
/* *** Parallel iterations over mempool items. *** */
-static void task_mempool_iter_func(void *userdata, MempoolIterData *item)
+static void task_mempool_iter_func(void *userdata,
+ MempoolIterData *item,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
int *data = (int *)item;
int *count = (int *)userdata;
@@ -119,7 +121,10 @@ TEST(task, MempoolIter)
}
}
- BLI_task_parallel_mempool(mempool, &num_items, task_mempool_iter_func, true);
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+
+ BLI_task_parallel_mempool(mempool, &num_items, task_mempool_iter_func, &settings);
/* Those checks should ensure us all items of the mempool were processed once, and only once - as
* expected. */
@@ -134,6 +139,100 @@ TEST(task, MempoolIter)
BLI_threadapi_exit();
}
+/* *** Parallel iterations over mempool items with TLS. *** */
+
+typedef struct TaskMemPool_Chunk {
+ ListBase *accumulate_items;
+} TaskMemPool_Chunk;
+
+static void task_mempool_iter_tls_func(void *UNUSED(userdata),
+ MempoolIterData *item,
+ const TaskParallelTLS *__restrict tls)
+{
+ TaskMemPool_Chunk *task_data = (TaskMemPool_Chunk *)tls->userdata_chunk;
+ int *data = (int *)item;
+
+ EXPECT_TRUE(data != nullptr);
+ if (task_data->accumulate_items == nullptr) {
+ task_data->accumulate_items = (ListBase *)MEM_callocN(sizeof(ListBase), __func__);
+ }
+
+ /* Flip to prove this has been touched. */
+ *data = -*data;
+
+ BLI_addtail(task_data->accumulate_items, BLI_genericNodeN(data));
+}
+
+static void task_mempool_iter_tls_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ TaskMemPool_Chunk *join_chunk = (TaskMemPool_Chunk *)chunk_join;
+ TaskMemPool_Chunk *data_chunk = (TaskMemPool_Chunk *)chunk;
+
+ if (data_chunk->accumulate_items != nullptr) {
+ if (join_chunk->accumulate_items == nullptr) {
+ join_chunk->accumulate_items = (ListBase *)MEM_callocN(sizeof(ListBase), __func__);
+ }
+ BLI_movelisttolist(join_chunk->accumulate_items, data_chunk->accumulate_items);
+ }
+}
+
+static void task_mempool_iter_tls_free(const void *UNUSED(userdata),
+ void *__restrict userdata_chunk)
+{
+ TaskMemPool_Chunk *task_data = (TaskMemPool_Chunk *)userdata_chunk;
+ MEM_freeN(task_data->accumulate_items);
+}
+
+TEST(task, MempoolIterTLS)
+{
+ int *data[NUM_ITEMS];
+ BLI_threadapi_init();
+ BLI_mempool *mempool = BLI_mempool_create(
+ sizeof(*data[0]), NUM_ITEMS, 32, BLI_MEMPOOL_ALLOW_ITER);
+
+ int i;
+
+ /* Add numbers negative `1..NUM_ITEMS` inclusive. */
+ int num_items = 0;
+ for (i = 0; i < NUM_ITEMS; i++) {
+ data[i] = (int *)BLI_mempool_alloc(mempool);
+ *data[i] = -(i + 1);
+ num_items++;
+ }
+
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+
+ TaskMemPool_Chunk tls_data;
+ tls_data.accumulate_items = NULL;
+
+ settings.userdata_chunk = &tls_data;
+ settings.userdata_chunk_size = sizeof(tls_data);
+
+ settings.func_free = task_mempool_iter_tls_free;
+ settings.func_reduce = task_mempool_iter_tls_reduce;
+
+ BLI_task_parallel_mempool(mempool, nullptr, task_mempool_iter_tls_func, &settings);
+
+ EXPECT_EQ(BLI_listbase_count(tls_data.accumulate_items), NUM_ITEMS);
+
+ /* Check that all elements are added into the list once. */
+ int num_accum = 0;
+ for (LinkData *link = (LinkData *)tls_data.accumulate_items->first; link; link = link->next) {
+ int *data = (int *)link->data;
+ num_accum += *data;
+ }
+ EXPECT_EQ(num_accum, (NUM_ITEMS * (NUM_ITEMS + 1)) / 2);
+
+ BLI_freelistN(tls_data.accumulate_items);
+ MEM_freeN(tls_data.accumulate_items);
+
+ BLI_mempool_destroy(mempool);
+ BLI_threadapi_exit();
+}
+
/* *** Parallel iterations over double-linked list items. *** */
static void task_listbase_iter_func(void *userdata,
diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc
index bbbe96f9b7e..c4016ca75e1 100644
--- a/source/blender/blenlib/tests/BLI_vector_set_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc
@@ -232,4 +232,43 @@ TEST(vector_set, PopExceptions)
EXPECT_EQ(set.size(), 4);
}
+TEST(vector_set, IndexOfOrAdd)
+{
+ VectorSet<int> set;
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(2), 1);
+ EXPECT_EQ(set.index_of_or_add(0), 2);
+ EXPECT_EQ(set.index_of_or_add(2), 1);
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(5), 3);
+ EXPECT_EQ(set.index_of_or_add(8), 4);
+ EXPECT_EQ(set.index_of_or_add(5), 3);
+}
+
+TEST(vector_set, Clear)
+{
+ VectorSet<int> set = {4, 6, 2, 4};
+ EXPECT_EQ(set.size(), 3);
+ set.clear();
+ EXPECT_EQ(set.size(), 0);
+ set.add_multiple({4, 1, 6, 8, 3, 6, 9, 3});
+ EXPECT_EQ(set.size(), 6);
+ set.clear();
+ EXPECT_EQ(set.size(), 0);
+}
+
+TEST(vector_set, LookupKey)
+{
+ VectorSet<std::string> set;
+ set.add("a");
+ set.add("b");
+ set.add("c");
+ EXPECT_EQ(set.lookup_key("a"), "a");
+ EXPECT_EQ(set.lookup_key_as("c"), "c");
+ EXPECT_EQ(set.lookup_key_ptr_as("d"), nullptr);
+ EXPECT_EQ(set.lookup_key_ptr_as("b")->size(), 1);
+ EXPECT_EQ(set.lookup_key_ptr("a"), set.lookup_key_ptr_as("a"));
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc
index 462f13c15ab..e8636168308 100644
--- a/source/blender/blenlib/tests/BLI_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_test.cc
@@ -248,6 +248,15 @@ TEST(vector, Append)
EXPECT_EQ(vec[2], 7);
}
+TEST(vector, AppendAs)
+{
+ Vector<StringRef> vec;
+ vec.append_as("hello", 2);
+ vec.append_as("world", 3);
+ EXPECT_EQ(vec[0], "he");
+ EXPECT_EQ(vec[1], "wor");
+}
+
TEST(vector, AppendAndGetIndex)
{
Vector<int> vec;
diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc
index ac25229cd69..a6d2ca10315 100644
--- a/source/blender/blenlib/tests/BLI_virtual_array_test.cc
+++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc
@@ -1,26 +1,29 @@
/* Apache License, Version 2.0 */
+#include "BLI_array.hh"
#include "BLI_strict_flags.h"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
#include "BLI_virtual_array.hh"
#include "testing/testing.h"
namespace blender::tests {
-TEST(virtual_array, ForSpan)
+TEST(virtual_array, Span)
{
std::array<int, 5> data = {3, 4, 5, 6, 7};
- VArrayForSpan<int> varray{data};
+ VArray_For_Span<int> varray{data};
EXPECT_EQ(varray.size(), 5);
EXPECT_EQ(varray.get(0), 3);
EXPECT_EQ(varray.get(4), 7);
EXPECT_TRUE(varray.is_span());
EXPECT_FALSE(varray.is_single());
- EXPECT_EQ(varray.get_span().data(), data.data());
+ EXPECT_EQ(varray.get_internal_span().data(), data.data());
}
-TEST(virtual_array, ForSingle)
+TEST(virtual_array, Single)
{
- VArrayForSingle<int> varray{10, 4};
+ VArray_For_Single<int> varray{10, 4};
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray.get(0), 10);
EXPECT_EQ(varray.get(3), 10);
@@ -28,4 +31,124 @@ TEST(virtual_array, ForSingle)
EXPECT_TRUE(varray.is_single());
}
+TEST(virtual_array, Array)
+{
+ Array<int> array = {1, 2, 3, 5, 8};
+ {
+ VArray_For_ArrayContainer varray{array};
+ EXPECT_EQ(varray.size(), 5);
+ EXPECT_EQ(varray[0], 1);
+ EXPECT_EQ(varray[2], 3);
+ EXPECT_EQ(varray[3], 5);
+ EXPECT_TRUE(varray.is_span());
+ }
+ {
+ VArray_For_ArrayContainer varray{std::move(array)};
+ EXPECT_EQ(varray.size(), 5);
+ EXPECT_EQ(varray[0], 1);
+ EXPECT_EQ(varray[2], 3);
+ EXPECT_EQ(varray[3], 5);
+ EXPECT_TRUE(varray.is_span());
+ }
+ {
+ VArray_For_ArrayContainer varray{array}; /* NOLINT: bugprone-use-after-move */
+ EXPECT_TRUE(varray.is_empty());
+ }
+}
+
+TEST(virtual_array, Vector)
+{
+ Vector<int> vector = {9, 8, 7, 6};
+ VArray_For_ArrayContainer varray{std::move(vector)};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 9);
+ EXPECT_EQ(varray[3], 6);
+}
+
+TEST(virtual_array, StdVector)
+{
+ std::vector<int> vector = {5, 6, 7, 8};
+ VArray_For_ArrayContainer varray{std::move(vector)};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 5);
+ EXPECT_EQ(varray[1], 6);
+}
+
+TEST(virtual_array, StdArray)
+{
+ std::array<int, 4> array = {2, 3, 4, 5};
+ VArray_For_ArrayContainer varray{array};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 2);
+ EXPECT_EQ(varray[1], 3);
+}
+
+TEST(virtual_array, VectorSet)
+{
+ VectorSet<int> vector_set = {5, 3, 7, 3, 3, 5, 1};
+ VArray_For_ArrayContainer varray{std::move(vector_set)};
+ EXPECT_TRUE(vector_set.is_empty()); /* NOLINT: bugprone-use-after-move. */
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 5);
+ EXPECT_EQ(varray[1], 3);
+ EXPECT_EQ(varray[2], 7);
+ EXPECT_EQ(varray[3], 1);
+}
+
+TEST(virtual_array, Func)
+{
+ auto func = [](int64_t index) { return (int)(index * index); };
+ VArray_For_Func<int, decltype(func)> varray{10, func};
+ EXPECT_EQ(varray.size(), 10);
+ EXPECT_EQ(varray[0], 0);
+ EXPECT_EQ(varray[3], 9);
+ EXPECT_EQ(varray[9], 81);
+}
+
+TEST(virtual_array, AsSpan)
+{
+ auto func = [](int64_t index) { return (int)(10 * index); };
+ VArray_For_Func<int, decltype(func)> func_varray{10, func};
+ VArray_Span span_varray{func_varray};
+ EXPECT_EQ(span_varray.size(), 10);
+ Span<int> span = span_varray;
+ EXPECT_EQ(span.size(), 10);
+ EXPECT_EQ(span[0], 0);
+ EXPECT_EQ(span[3], 30);
+ EXPECT_EQ(span[6], 60);
+}
+
+static int get_x(const std::array<int, 3> &item)
+{
+ return item[0];
+}
+
+static void set_x(std::array<int, 3> &item, int value)
+{
+ item[0] = value;
+}
+
+TEST(virtual_array, DerivedSpan)
+{
+ Vector<std::array<int, 3>> vector;
+ vector.append({3, 4, 5});
+ vector.append({1, 1, 1});
+ {
+ VArray_For_DerivedSpan<std::array<int, 3>, int, get_x> varray{vector};
+ EXPECT_EQ(varray.size(), 2);
+ EXPECT_EQ(varray[0], 3);
+ EXPECT_EQ(varray[1], 1);
+ }
+ {
+ VMutableArray_For_DerivedSpan<std::array<int, 3>, int, get_x, set_x> varray{vector};
+ EXPECT_EQ(varray.size(), 2);
+ EXPECT_EQ(varray[0], 3);
+ EXPECT_EQ(varray[1], 1);
+ varray.set(0, 10);
+ varray.set(1, 20);
+ EXPECT_EQ(vector[0][0], 10);
+ EXPECT_EQ(vector[1][0], 20);
+ }
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index d8c613ba900..89db216aada 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -226,7 +226,10 @@ int BLO_library_link_copypaste(struct Main *mainl, BlendHandle *bh, const uint64
* Struct for temporarily loading datablocks from a blend file.
*/
typedef struct TempLibraryContext {
- struct Main *temp_main;
+ /** Temporary main used for library data. */
+ struct Main *bmain_lib;
+ /** Temporary main used to load data into (currently initialized from `real_main`). */
+ struct Main *bmain_base;
struct BlendHandle *blendhandle;
struct LibraryLink_Params liblink_params;
struct Library *lib;
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index ee9b9a49768..36802fc8842 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -57,6 +57,7 @@ set(SRC
intern/versioning_270.c
intern/versioning_280.c
intern/versioning_290.c
+ intern/versioning_300.c
intern/versioning_cycles.c
intern/versioning_defaults.c
intern/versioning_dna.c
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index b657cb8b2f9..47ed4e5c06f 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -62,6 +62,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_ghash.h"
#include "BLI_linklist.h"
@@ -111,6 +112,7 @@
#include "SEQ_iterator.h"
#include "SEQ_modifier.h"
#include "SEQ_sequencer.h"
+#include "SEQ_utils.h"
#include "readfile.h"
@@ -2696,7 +2698,7 @@ static int lib_link_seq_clipboard_cb(Sequence *seq, void *arg_pt)
static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map)
{
/* update IDs stored in sequencer clipboard */
- SEQ_iterator_seqbase_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map);
+ SEQ_seqbase_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map);
}
static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data)
@@ -2912,7 +2914,7 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
else if (sl->spacetype == SPACE_TEXT) {
SpaceText *st = (SpaceText *)sl;
- st->text = restore_pointer_by_name(id_map, (ID *)st->text, USER_REAL);
+ st->text = restore_pointer_by_name(id_map, (ID *)st->text, USER_IGNORE);
if (st->text == NULL) {
st->text = newmain->texts.first;
}
@@ -3010,8 +3012,13 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
else if (sl->spacetype == SPACE_SPREADSHEET) {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- sspreadsheet->pinned_id = restore_pointer_by_name(
- id_map, sspreadsheet->pinned_id, USER_IGNORE);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ object_context->object = restore_pointer_by_name(
+ id_map, (ID *)object_context->object, USER_IGNORE);
+ }
+ }
}
}
}
@@ -3863,6 +3870,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
blo_do_versions_270(fd, lib, main);
blo_do_versions_280(fd, lib, main);
blo_do_versions_290(fd, lib, main);
+ blo_do_versions_300(fd, lib, main);
blo_do_versions_cycles(fd, lib, main);
/* WATCH IT!!!: pointers from libdata have not been converted yet here! */
@@ -3886,6 +3894,7 @@ static void do_versions_after_linking(Main *main, ReportList *reports)
do_versions_after_linking_270(main);
do_versions_after_linking_280(main, reports);
do_versions_after_linking_290(main, reports);
+ do_versions_after_linking_300(main, reports);
do_versions_after_linking_cycles(main);
main->is_locked_for_linking = false;
@@ -4444,7 +4453,9 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
if (id == NULL) {
/* ID has not been read yet, add placeholder to the main of the
* library it belongs to, so that it will be read later. */
- read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, NULL);
+ read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, &id);
+ id_sort_by_name(which_libbase(libmain, GS(id->name)), id, id->prev);
+
/* commented because this can print way too much */
// if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->filepath);
@@ -4504,7 +4515,8 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
bhead,
fd->id_tag_extra | LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT,
false,
- NULL);
+ &id);
+ id_sort_by_name(which_libbase(mainvar, GS(id->name)), id, id->prev);
}
else {
/* Convert any previously read weak link to regular link
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index 9682b5456d2..d1d4e0b3256 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -210,6 +210,7 @@ void blo_do_versions_260(struct FileData *fd, struct Library *lib, struct Main *
void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_280(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_290(struct FileData *fd, struct Library *lib, struct Main *bmain);
+void blo_do_versions_300(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_cycles(struct FileData *fd, struct Library *lib, struct Main *bmain);
void do_versions_after_linking_250(struct Main *bmain);
@@ -217,6 +218,7 @@ void do_versions_after_linking_260(struct Main *bmain);
void do_versions_after_linking_270(struct Main *bmain);
void do_versions_after_linking_280(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_290(struct Main *bmain, struct ReportList *reports);
+void do_versions_after_linking_300(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_cycles(struct Main *bmain);
/* This is rather unfortunate to have to expose this here, but better use that nasty hack in
diff --git a/source/blender/blenloader/intern/readfile_tempload.c b/source/blender/blenloader/intern/readfile_tempload.c
index 691d8f542ab..4566e1e9b4d 100644
--- a/source/blender/blenloader/intern/readfile_tempload.c
+++ b/source/blender/blenloader/intern/readfile_tempload.c
@@ -21,6 +21,9 @@
#include "MEM_guardedalloc.h"
+#include "BLI_string.h"
+
+#include "BKE_main.h"
#include "BKE_report.h"
#include "DNA_ID.h"
@@ -32,15 +35,20 @@ TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
struct ReportList *reports)
{
TempLibraryContext *temp_lib_ctx = MEM_callocN(sizeof(*temp_lib_ctx), __func__);
+ temp_lib_ctx->bmain_base = BKE_main_new();
+
+ /* Copy the file path so any path remapping is performed properly. */
+ STRNCPY(temp_lib_ctx->bmain_base->name, real_main->name);
temp_lib_ctx->blendhandle = BLO_blendhandle_from_file(blend_file_path, reports);
- BLO_library_link_params_init(&temp_lib_ctx->liblink_params, real_main, 0, LIB_TAG_TEMP_MAIN);
+ BLO_library_link_params_init(
+ &temp_lib_ctx->liblink_params, temp_lib_ctx->bmain_base, 0, LIB_TAG_TEMP_MAIN);
- temp_lib_ctx->temp_main = BLO_library_link_begin(
+ temp_lib_ctx->bmain_lib = BLO_library_link_begin(
&temp_lib_ctx->blendhandle, blend_file_path, &temp_lib_ctx->liblink_params);
- temp_lib_ctx->temp_id = BLO_library_link_named_part(temp_lib_ctx->temp_main,
+ temp_lib_ctx->temp_id = BLO_library_link_named_part(temp_lib_ctx->bmain_lib,
&temp_lib_ctx->blendhandle,
idcode,
idname,
@@ -51,8 +59,13 @@ TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
void BLO_library_temp_free(TempLibraryContext *temp_lib_ctx)
{
+ /* This moves the temporary ID and any indirectly loaded data into `bmain_base`
+ * only to free `bmain_base`, while redundant this is the typical code-path for library linking,
+ * it's more convenient to follow this convention rather than create a new code-path for this
+ * one-off use case. */
BLO_library_link_end(
- temp_lib_ctx->temp_main, &temp_lib_ctx->blendhandle, &temp_lib_ctx->liblink_params);
+ temp_lib_ctx->bmain_lib, &temp_lib_ctx->blendhandle, &temp_lib_ctx->liblink_params);
BLO_blendhandle_close(temp_lib_ctx->blendhandle);
+ BKE_main_free(temp_lib_ctx->bmain_base);
MEM_freeN(temp_lib_ctx);
}
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 467fd8b0399..990fc1d65d7 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -446,7 +446,7 @@ static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name)
id->flag = LIB_FAKEUSER;
*((short *)id->name) = ID_GD;
- BKE_id_new_name_validate(lb, id, name);
+ BKE_id_new_name_validate(lb, id, name, false);
/* alphabetic insertion: is in BKE_id_new_name_validate */
if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) {
@@ -779,7 +779,6 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
Nurb *nu;
for (nu = cu->nurb.first; nu; nu = nu->next) {
- nu->flag |= (nu->type & CU_2D);
nu->type &= CU_TYPE;
}
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 1ecaee10e6a..cf8f45ca227 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -1814,7 +1814,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 280, 1)) {
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "bleedexp")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "bleedexp")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->bleedexp = 2.5f;
}
@@ -1840,7 +1840,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 280, 2)) {
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "cascade_max_dist")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "cascade_max_dist")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->cascade_max_dist = 1000.0f;
la->cascade_count = 4;
@@ -1849,7 +1849,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "contact_dist")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "contact_dist")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->contact_dist = 0.2f;
la->contact_bias = 0.03f;
@@ -2185,7 +2185,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 280, 13)) {
/* Initialize specular factor. */
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "spec_fac")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "spec_fac")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->spec_fac = 1.0f;
}
@@ -3135,7 +3135,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "att_dist")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "att_dist")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->att_dist = la->clipend;
}
@@ -3172,7 +3172,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
bool is_blend = false;
{
- char tool = tool_init;
+ char tool;
switch (tool_init) {
case PAINT_BLEND_MIX:
tool = VPAINT_TOOL_DRAW;
@@ -4051,7 +4051,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
/* Initializes sun lights with the new angular diameter property */
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "sun_angle")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "sun_angle")) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->sun_angle = 2.0f * atanf(light->area_size);
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 31e4b659c2f..565e62158ff 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -27,6 +27,7 @@
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_collection_types.h"
@@ -47,6 +48,7 @@
#include "DNA_screen_types.h"
#include "DNA_shader_fx_types.h"
#include "DNA_space_types.h"
+#include "DNA_text_types.h"
#include "DNA_tracking_types.h"
#include "DNA_workspace_types.h"
@@ -56,6 +58,7 @@
#include "BKE_collection.h"
#include "BKE_colortools.h"
#include "BKE_cryptomatte.h"
+#include "BKE_curve.h"
#include "BKE_fcurve.h"
#include "BKE_gpencil.h"
#include "BKE_lib_id.h"
@@ -377,6 +380,37 @@ static void seq_update_meta_disp_range(Editing *ed)
}
}
+static void version_node_socket_duplicate(bNodeTree *ntree,
+ const int node_type,
+ const char *old_name,
+ const char *new_name)
+{
+ /* Duplicate a link going into the original socket. */
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
+ if (link->tonode->type == node_type) {
+ bNode *node = link->tonode;
+ bNodeSocket *dest_socket = nodeFindSocket(node, SOCK_IN, new_name);
+ BLI_assert(dest_socket);
+ if (STREQ(link->tosock->name, old_name)) {
+ nodeAddLink(ntree, link->fromnode, link->fromsock, node, dest_socket);
+ }
+ }
+ }
+
+ /* Duplicate the default value from the old socket and assign it to the new socket. */
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == node_type) {
+ bNodeSocket *source_socket = nodeFindSocket(node, SOCK_IN, old_name);
+ bNodeSocket *dest_socket = nodeFindSocket(node, SOCK_IN, new_name);
+ BLI_assert(source_socket && dest_socket);
+ if (dest_socket->default_value) {
+ MEM_freeN(dest_socket->default_value);
+ }
+ dest_socket->default_value = MEM_dupallocN(source_socket->default_value);
+ }
+ }
+}
+
void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
{
if (!MAIN_VERSION_ATLEAST(bmain, 290, 1)) {
@@ -639,6 +673,29 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 16)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ seq_update_meta_disp_range(SEQ_editing_get(scene, false));
+ }
+
+ /* Add a separate socket for Grid node X and Y size. */
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_socket_duplicate(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Size X", "Size Y");
+ }
+ FOREACH_NODETREE_END;
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 20)) {
+ /* Set zero user text objects to have a fake user. */
+ LISTBASE_FOREACH (Text *, text, &bmain->texts) {
+ if (text->id.us == 0) {
+ id_fake_user_set(&text->id);
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -651,10 +708,6 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
-
- LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- seq_update_meta_disp_range(SEQ_editing_get(scene, false));
- }
}
}
@@ -1531,7 +1584,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->ed != NULL) {
+ if (scene->toolsettings->sequencer_tool_settings == NULL) {
scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
}
}
@@ -1907,7 +1960,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 293, 14)) {
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "diff_fac")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "diff_fac")) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->diff_fac = 1.0f;
light->volume_fac = 1.0f;
@@ -1925,6 +1978,115 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 15)) {
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (STREQ(node->idname, "GeometryNodeMeshPlane")) {
+ STRNCPY(node->idname, "GeometryNodeMeshGrid");
+ }
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 16)) {
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Size", "Size X");
+ }
+ FOREACH_NODETREE_END;
+ }
+
+ /* The CU_2D flag has been removed. */
+ LISTBASE_FOREACH (Curve *, cu, &bmain->curves) {
+#define CU_2D (1 << 3)
+ ListBase *nurbs = BKE_curve_nurbs_get(cu);
+ bool is_2d = true;
+
+ LISTBASE_FOREACH (Nurb *, nu, nurbs) {
+ if (nu->flag & CU_2D) {
+ nu->flag &= ~CU_2D;
+ }
+ else {
+ is_2d = false;
+ }
+ }
+#undef CU_2D
+ if (!is_2d && CU_IS_2D(cu)) {
+ cu->flag |= CU_3D;
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) {
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density");
+ }
+ }
+ FOREACH_NODETREE_END;
+
+ if (!DNA_struct_elem_find(fd->filesdna, "bArmature", "float", "axes_position")) {
+ /* Convert the axes draw position to its old default (tip of bone). */
+ LISTBASE_FOREACH (struct bArmature *, arm, &bmain->armatures) {
+ arm->axes_position = 1.0;
+ }
+ }
+
+ /* Initialize the spread parameter for area lights*/
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "area_spread")) {
+ LISTBASE_FOREACH (Light *, la, &bmain->lights) {
+ la->area_spread = DEG2RADF(180.0f);
+ }
+ }
+
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_NODE) {
+ SpaceNode *snode = (SpaceNode *)sl;
+ LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) {
+ STRNCPY(path->display_name, path->node_name);
+ }
+ }
+ }
+ }
+ }
+
+ /* Consolidate node and final evaluation modes. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ if (sspreadsheet->object_eval_state == 2) {
+ sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Set default value for the new bisect_threshold parameter in the mirror modifier. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 19)) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Mirror) {
+ MirrorModifierData *mmd = (MirrorModifierData *)md;
+ /* This was the previous hard-coded value. */
+ mmd->bisect_threshold = 0.001f;
+ }
+ }
+ }
+
+ LISTBASE_FOREACH (Curve *, cu, &bmain->curves) {
+ /* Turn on clamping as this was implicit before. */
+ cu->flag |= CU_PATH_CLAMP;
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
new file mode 100644
index 00000000000..268598ccc51
--- /dev/null
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -0,0 +1,263 @@
+/*
+ * 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 blenloader
+ */
+/* allow readfile to use deprecated functionality */
+#define DNA_DEPRECATED_ALLOW
+
+#include "BLI_listbase.h"
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_genfile.h"
+#include "DNA_listBase.h"
+#include "DNA_modifier_types.h"
+#include "DNA_text_types.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+
+#include "BLO_readfile.h"
+#include "readfile.h"
+
+static void sort_linked_ids(Main *bmain)
+{
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ ListBase temp_list;
+ BLI_listbase_clear(&temp_list);
+ LISTBASE_FOREACH_MUTABLE (ID *, id, lb) {
+ if (ID_IS_LINKED(id)) {
+ BLI_remlink(lb, id);
+ BLI_addtail(&temp_list, id);
+ id_sort_by_name(&temp_list, id, NULL);
+ }
+ }
+ BLI_movelisttolist(lb, &temp_list);
+ }
+ FOREACH_MAIN_LISTBASE_END;
+}
+
+static void assert_sorted_ids(Main *bmain)
+{
+#ifndef NDEBUG
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ ID *id_prev = NULL;
+ LISTBASE_FOREACH (ID *, id, lb) {
+ if (id_prev == NULL) {
+ continue;
+ }
+ BLI_assert(id_prev->lib != id->lib || BLI_strcasecmp(id_prev->name, id->name) < 0);
+ }
+ }
+ FOREACH_MAIN_LISTBASE_END;
+#else
+ UNUSED_VARS_NDEBUG(bmain);
+#endif
+}
+
+void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
+{
+ if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
+ /* Set zero user text objects to have a fake user. */
+ LISTBASE_FOREACH (Text *, text, &bmain->texts) {
+ if (text->id.us == 0) {
+ id_fake_user_set(&text->id);
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 3)) {
+ /* Use new texture socket in Attribute Sample Texture node. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) {
+ continue;
+ }
+ if (node->id == NULL) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->type == SOCK_TEXTURE) {
+ bNodeSocketValueTexture *socket_value = (bNodeSocketValueTexture *)
+ socket->default_value;
+ socket_value->value = (Tex *)node->id;
+ break;
+ }
+ }
+ node->id = NULL;
+ }
+ }
+
+ sort_linked_ids(bmain);
+ assert_sorted_ids(bmain);
+ }
+ if (MAIN_VERSION_ATLEAST(bmain, 300, 3)) {
+ assert_sorted_ids(bmain);
+ }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #blo_do_versions_300 in this file.
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
+}
+
+static void version_switch_node_input_prefix(Main *bmain)
+{
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == GEO_NODE_SWITCH) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ /* Skip the "switch" socket. */
+ if (socket == node->inputs.first) {
+ continue;
+ }
+ strcpy(socket->name, socket->name[0] == 'A' ? "False" : "True");
+
+ /* Replace "A" and "B", but keep the unique number suffix at the end. */
+ char number_suffix[8];
+ BLI_strncpy(number_suffix, socket->identifier + 1, sizeof(number_suffix));
+ strcpy(socket->identifier, socket->name);
+ strcat(socket->identifier, number_suffix);
+ }
+ }
+ }
+ }
+ }
+ FOREACH_NODETREE_END;
+}
+
+static void version_node_socket_name(bNodeTree *ntree,
+ const int node_type,
+ const char *old_name,
+ const char *new_name)
+{
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == node_type) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (STREQ(socket->name, old_name)) {
+ strcpy(socket->name, new_name);
+ }
+ if (STREQ(socket->identifier, old_name)) {
+ strcpy(socket->identifier, new_name);
+ }
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ if (STREQ(socket->name, old_name)) {
+ strcpy(socket->name, new_name);
+ }
+ if (STREQ(socket->identifier, old_name)) {
+ strcpy(socket->identifier, new_name);
+ }
+ }
+ }
+ }
+}
+
+/* NOLINTNEXTLINE: readability-function-size */
+void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
+{
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
+ /* Set default value for the new bisect_threshold parameter in the mirror modifier. */
+ if (!DNA_struct_elem_find(fd->filesdna, "MirrorModifierData", "float", "bisect_threshold")) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Mirror) {
+ MirrorModifierData *mmd = (MirrorModifierData *)md;
+ /* This was the previous hard-coded value. */
+ mmd->bisect_threshold = 0.001f;
+ }
+ }
+ }
+ }
+ /* Grease Pencil: Set default value for dilate pixels. */
+ if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "int", "dilate_pixels")) {
+ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
+ if (brush->gpencil_settings) {
+ brush->gpencil_settings->dilate_pixels = 1;
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 2)) {
+ version_switch_node_input_prefix(bmain);
+
+ if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->pose == NULL) {
+ continue;
+ }
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale);
+ }
+ }
+ }
+ }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_socket_name(ntree, GEO_NODE_BOUNDING_BOX, "Mesh", "Bounding Box");
+ }
+ }
+ FOREACH_NODETREE_END;
+
+ if (!DNA_struct_elem_find(fd->filesdna, "FileAssetSelectParams", "int", "import_type")) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_FILE) {
+ SpaceFile *sfile = (SpaceFile *)sl;
+ if (sfile->asset_params) {
+ sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index e43f8153bd1..56b2f18f8a6 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -81,6 +81,7 @@
#include "BKE_pointcache.h"
#include "SEQ_iterator.h"
+#include "SEQ_sequencer.h"
#include "NOD_socket.h"
@@ -1277,12 +1278,6 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (ob->soft->physics_speed == 0.0f) {
ob->soft->physics_speed = 1.0f;
}
-
- if (ob->soft->interval == 0) {
- ob->soft->interval = 2;
- ob->soft->sfra = 1;
- ob->soft->efra = 100;
- }
}
if (ob->soft && ob->soft->vertgroup == 0) {
bDeformGroup *locGroup = BKE_object_defgroup_find_name(ob, "SOFTGOAL");
@@ -2560,18 +2555,16 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (bmain->versionfile < 249 && bmain->subversionfile < 2) {
Scene *sce = bmain->scenes.first;
- Sequence *seq;
Editing *ed;
while (sce) {
ed = sce->ed;
if (ed) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->strip && seq->strip->proxy) {
seq->strip->proxy->quality = 90;
}
}
- SEQ_CURRENT_END;
}
sce = sce->id.next;
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 8cbedb05931..be3834faa5a 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -24,6 +24,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#ifdef WITH_INTERNATIONAL
@@ -261,17 +262,7 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
FROM_DEFAULT_V4_UCHAR(space_node.nodeclass_shader);
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - #blo_do_versions_userdef in this file.
- * - "versioning_{BLENDER_VERSION}.c"
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
+ if (!USER_VERSION_ATLEAST(293, 15)) {
FROM_DEFAULT_V4_UCHAR(space_properties.active);
FROM_DEFAULT_V4_UCHAR(space_info.info_error);
@@ -286,6 +277,19 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
btheme->space_spreadsheet = btheme->space_outliner;
}
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #blo_do_versions_userdef in this file.
+ * - "versioning_{BLENDER_VERSION}.c"
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
+
#undef FROM_DEFAULT_V4_UCHAR
#undef USER_VERSION_ATLEAST
@@ -838,6 +842,23 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
+ if (!USER_VERSION_ATLEAST(293, 1)) {
+ /* This rename was made after 2.93.0, harmless to run when it's not needed. */
+ const char *replace_table[][2] = {
+ {"blender", "Blender"},
+ {"blender_27x", "Blender_27x"},
+ {"industry_compatible", "Industry_Compatible"},
+ };
+ const int replace_table_len = ARRAY_SIZE(replace_table);
+
+ BLI_str_replace_table_exact(
+ userdef->keyconfigstr, sizeof(userdef->keyconfigstr), replace_table, replace_table_len);
+ LISTBASE_FOREACH (wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) {
+ BLI_str_replace_table_exact(
+ kpt->idname, sizeof(kpt->idname), replace_table, replace_table_len);
+ }
+ }
+
if (!USER_VERSION_ATLEAST(293, 2)) {
/* Enable asset browser features by default for alpha testing.
* BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 4ac49d5aebb..38c00b3f132 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -100,6 +100,7 @@
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
+#include "BLI_endian_defines.h"
#include "BLI_mempool.h"
#include "MEM_guardedalloc.h" /* MEM_freeN */
diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
index 8d8dc3aebf7..280a4b42b36 100644
--- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc
+++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
@@ -50,10 +50,6 @@
#include "CLG_log.h"
-BlendfileLoadingBaseTest::~BlendfileLoadingBaseTest()
-{
-}
-
void BlendfileLoadingBaseTest::SetUpTestCase()
{
testing::Test::SetUpTestCase();
diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.h b/source/blender/blenloader/tests/blendfile_loading_base_test.h
index 32bb2959dcc..d352aecfe41 100644
--- a/source/blender/blenloader/tests/blendfile_loading_base_test.h
+++ b/source/blender/blenloader/tests/blendfile_loading_base_test.h
@@ -30,8 +30,6 @@ class BlendfileLoadingBaseTest : public testing::Test {
struct Depsgraph *depsgraph = nullptr;
public:
- virtual ~BlendfileLoadingBaseTest();
-
/* Sets up Blender just enough to not crash on loading
* a blendfile and constructing a depsgraph. */
static void SetUpTestCase();
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index c215cf69e3a..ec282888ffa 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -102,6 +102,12 @@ set(SRC
intern/bmesh_mesh_convert.h
intern/bmesh_mesh_duplicate.c
intern/bmesh_mesh_duplicate.h
+ intern/bmesh_mesh_normals.c
+ intern/bmesh_mesh_normals.h
+ intern/bmesh_mesh_partial_update.c
+ intern/bmesh_mesh_partial_update.h
+ intern/bmesh_mesh_tessellate.c
+ intern/bmesh_mesh_tessellate.h
intern/bmesh_mesh_validate.c
intern/bmesh_mesh_validate.h
intern/bmesh_mods.c
@@ -182,10 +188,6 @@ set(LIB
extern_rangetree
)
-if(MSVC AND NOT MSVC_CLANG)
- string(APPEND CMAKE_C_FLAGS " /WX /wd4101")
-endif()
-
if(WITH_BULLET)
list(APPEND INC_SYS
${BULLET_INCLUDE_DIRS}
@@ -225,6 +227,10 @@ endif()
blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+if(MSVC AND NOT MSVC_CLANG)
+ target_compile_options(bf_bmesh PRIVATE /WX /wd4101)
+endif()
+
if(WITH_GTESTS)
set(TEST_SRC
tests/bmesh_core_test.cc
diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h
index 4441ccc0c88..a5241f6b36d 100644
--- a/source/blender/bmesh/bmesh.h
+++ b/source/blender/bmesh/bmesh.h
@@ -215,6 +215,9 @@ extern "C" {
#include "intern/bmesh_mesh.h"
#include "intern/bmesh_mesh_convert.h"
#include "intern/bmesh_mesh_duplicate.h"
+#include "intern/bmesh_mesh_normals.h"
+#include "intern/bmesh_mesh_partial_update.h"
+#include "intern/bmesh_mesh_tessellate.h"
#include "intern/bmesh_mesh_validate.h"
#include "intern/bmesh_mods.h"
#include "intern/bmesh_operators.h"
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index cf907862120..e72c689ddfb 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -1805,7 +1805,8 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
BMVert *v_kill,
const bool do_del,
const bool check_edge_exists,
- const bool kill_degenerate_faces)
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces)
{
BMEdge *e_old;
BMVert *v_old, *v_target;
@@ -1840,6 +1841,9 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
BMLoop *l_kill_next;
+ /* Candidates for being duplicate. */
+ BLI_SMALLSTACK_DECLARE(faces_duplicate_candidate, BMFace *);
+
#ifndef NDEBUG
/* For verification later, count valence of 'v_old' and 'v_target' */
valence1 = bmesh_disk_count(v_old);
@@ -1877,9 +1881,14 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
/* fix len attribute of face */
l_kill->f->len--;
- if (kill_degenerate_faces) {
- if (l_kill->f->len < 3) {
- BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
+ if (kill_degenerate_faces && (l_kill->f->len < 3)) {
+ BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
+ }
+ else {
+ /* The duplicate test isn't reliable at this point as `e_splice` might be set,
+ * so the duplicate test needs to run once the edge has been spliced. */
+ if (kill_duplicate_faces) {
+ BLI_SMALLSTACK_PUSH(faces_duplicate_candidate, l_kill->f);
}
}
l_kill_next = l_kill->radial_next;
@@ -1940,6 +1949,15 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
}
}
+ if (kill_duplicate_faces) {
+ BMFace *f_kill;
+ while ((f_kill = BLI_SMALLSTACK_POP(faces_duplicate_candidate))) {
+ if (BM_face_find_double(f_kill)) {
+ BM_face_kill(bm, f_kill);
+ }
+ }
+ }
+
BM_CHECK_ELEMENT(v_old);
BM_CHECK_ELEMENT(v_target);
BM_CHECK_ELEMENT(e_old);
diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h
index df73984e6cf..8f7580714ae 100644
--- a/source/blender/bmesh/intern/bmesh_core.h
+++ b/source/blender/bmesh/intern/bmesh_core.h
@@ -115,7 +115,8 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
BMVert *v_kill,
const bool do_del,
const bool check_edge_exists,
- const bool kill_degenerate_faces);
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces);
BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm,
BMEdge *e_kill,
BMVert *v_kill,
diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h
index c384fb03cd9..81b6a58e58b 100644
--- a/source/blender/bmesh/intern/bmesh_iterators_inline.h
+++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h
@@ -188,18 +188,18 @@ BLI_INLINE void BM_iter_parallel(BMesh *bm,
const char itype,
TaskParallelMempoolFunc func,
void *userdata,
- const bool use_threading)
+ const TaskParallelSettings *settings)
{
/* inlining optimizes out this switch when called with the defined type */
switch ((BMIterType)itype) {
case BM_VERTS_OF_MESH:
- BLI_task_parallel_mempool(bm->vpool, userdata, func, use_threading);
+ BLI_task_parallel_mempool(bm->vpool, userdata, func, settings);
break;
case BM_EDGES_OF_MESH:
- BLI_task_parallel_mempool(bm->epool, userdata, func, use_threading);
+ BLI_task_parallel_mempool(bm->epool, userdata, func, settings);
break;
case BM_FACES_OF_MESH:
- BLI_task_parallel_mempool(bm->fpool, userdata, func, use_threading);
+ BLI_task_parallel_mempool(bm->fpool, userdata, func, settings);
break;
default:
/* should never happen */
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index 5e879d41d43..d0c6bc83088 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -25,22 +25,14 @@
#include "DNA_listBase.h"
#include "DNA_scene_types.h"
-#include "BLI_bitmap.h"
-#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
-#include "BLI_stack.h"
-#include "BLI_task.h"
#include "BLI_utildefines.h"
-#include "BKE_editmesh.h"
-#include "BKE_global.h"
+#include "BKE_customdata.h"
#include "BKE_mesh.h"
-#include "BKE_multires.h"
-#include "atomic_ops.h"
-
-#include "intern/bmesh_private.h"
+#include "bmesh.h"
/* used as an extern, defined in bmesh.h */
const BMAllocTemplate bm_mesh_allocsize_default = {512, 1024, 2048, 512};
@@ -319,1653 +311,6 @@ void BM_mesh_free(BMesh *bm)
}
/**
- * Helpers for #BM_mesh_normals_update and #BM_verts_calc_normal_vcos
- */
-
-/* We use that existing internal API flag,
- * assuming no other tool using it would run concurrently to clnors editing. */
-#define BM_LNORSPACE_UPDATE _FLAG_MF
-
-typedef struct BMEdgesCalcVectorsData {
- /* Read-only data. */
- const float (*vcos)[3];
-
- /* Read-write data, but no need to protect it, no concurrency to fear here. */
- float (*edgevec)[3];
-} BMEdgesCalcVectorsData;
-
-static void mesh_edges_calc_vectors_cb(void *userdata, MempoolIterData *mp_e)
-{
- BMEdgesCalcVectorsData *data = userdata;
- BMEdge *e = (BMEdge *)mp_e;
-
- if (e->l) {
- const float *v1_co = data->vcos ? data->vcos[BM_elem_index_get(e->v1)] : e->v1->co;
- const float *v2_co = data->vcos ? data->vcos[BM_elem_index_get(e->v2)] : e->v2->co;
- sub_v3_v3v3(data->edgevec[BM_elem_index_get(e)], v2_co, v1_co);
- normalize_v3(data->edgevec[BM_elem_index_get(e)]);
- }
- else {
- /* the edge vector will not be needed when the edge has no radial */
- }
-}
-
-static void bm_mesh_edges_calc_vectors(BMesh *bm, float (*edgevec)[3], const float (*vcos)[3])
-{
- BM_mesh_elem_index_ensure(bm, BM_EDGE | (vcos ? BM_VERT : 0));
-
- BMEdgesCalcVectorsData data = {
- .vcos = vcos,
- .edgevec = edgevec,
- };
-
- BM_iter_parallel(
- bm, BM_EDGES_OF_MESH, mesh_edges_calc_vectors_cb, &data, bm->totedge >= BM_OMP_LIMIT);
-}
-
-typedef struct BMVertsCalcNormalsData {
- /* Read-only data. */
- const float (*fnos)[3];
- const float (*edgevec)[3];
- const float (*vcos)[3];
-
- /* Read-write data, protected by an atomic-based fake spin-lock like system. */
- float (*vnos)[3];
-} BMVertsCalcNormalsData;
-
-static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f)
-{
-#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb))
-
- BMVertsCalcNormalsData *data = userdata;
- BMFace *f = (BMFace *)mp_f;
-
- const float *f_no = data->fnos ? data->fnos[BM_elem_index_get(f)] : f->no;
-
- BMLoop *l_first, *l_iter;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- const float *e1diff, *e2diff;
- float dotprod;
- float fac;
-
- /* calculate the dot product of the two edges that
- * meet at the loop's vertex */
- e1diff = data->edgevec[BM_elem_index_get(l_iter->prev->e)];
- e2diff = data->edgevec[BM_elem_index_get(l_iter->e)];
- dotprod = dot_v3v3(e1diff, e2diff);
-
- /* edge vectors are calculated from e->v1 to e->v2, so
- * adjust the dot product if one but not both loops
- * actually runs from from e->v2 to e->v1 */
- if ((l_iter->prev->e->v1 == l_iter->prev->v) ^ (l_iter->e->v1 == l_iter->v)) {
- dotprod = -dotprod;
- }
-
- fac = saacos(-dotprod);
-
- if (fac != fac) { /* NAN detection. */
- /* Degenerated case, nothing to do here, just ignore that vertex. */
- continue;
- }
-
- /* accumulate weighted face normal into the vertex's normal */
- float *v_no = data->vnos ? data->vnos[BM_elem_index_get(l_iter->v)] : l_iter->v->no;
-
- /* This block is a lockless threadsafe madd_v3_v3fl.
- * It uses the first float of the vector as a sort of cheap spin-lock,
- * assuming FLT_MAX is a safe 'illegal' value that cannot be set here otherwise.
- * It also assumes that collisions between threads are highly unlikely,
- * else performances would be quite bad here. */
- float virtual_lock = v_no[0];
- while (true) {
- /* This loops until following conditions are met:
- * - v_no[0] has same value as virtual_lock (i.e. it did not change since last try).
- * - v_no[0] was not FLT_MAX, i.e. it was not locked by another thread.
- */
- const float vl = atomic_cas_float(&v_no[0], virtual_lock, FLT_MAX);
- if (FLT_EQ_NONAN(vl, virtual_lock) && vl != FLT_MAX) {
- break;
- }
- virtual_lock = vl;
- }
- BLI_assert(v_no[0] == FLT_MAX);
- /* Now we own that normal value, and can change it.
- * But first scalar of the vector must not be changed yet, it's our lock! */
- virtual_lock += f_no[0] * fac;
- v_no[1] += f_no[1] * fac;
- v_no[2] += f_no[2] * fac;
- /* Second atomic operation to 'release'
- * our lock on that vector and set its first scalar value. */
- /* Note that we do not need to loop here, since we 'locked' v_no[0],
- * nobody should have changed it in the mean time. */
- virtual_lock = atomic_cas_float(&v_no[0], FLT_MAX, virtual_lock);
- BLI_assert(virtual_lock == FLT_MAX);
-
- } while ((l_iter = l_iter->next) != l_first);
-
-#undef FLT_EQ_NONAN
-}
-
-static void mesh_verts_calc_normals_normalize_cb(void *userdata, MempoolIterData *mp_v)
-{
- BMVertsCalcNormalsData *data = userdata;
- BMVert *v = (BMVert *)mp_v;
-
- float *v_no = data->vnos ? data->vnos[BM_elem_index_get(v)] : v->no;
- if (UNLIKELY(normalize_v3(v_no) == 0.0f)) {
- const float *v_co = data->vcos ? data->vcos[BM_elem_index_get(v)] : v->co;
- normalize_v3_v3(v_no, v_co);
- }
-}
-
-static void bm_mesh_verts_calc_normals(BMesh *bm,
- const float (*edgevec)[3],
- const float (*fnos)[3],
- const float (*vcos)[3],
- float (*vnos)[3])
-{
- BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE) | ((vnos || vcos) ? BM_VERT : 0));
-
- BMVertsCalcNormalsData data = {
- .fnos = fnos,
- .edgevec = edgevec,
- .vcos = vcos,
- .vnos = vnos,
- };
-
- BM_iter_parallel(
- bm, BM_FACES_OF_MESH, mesh_verts_calc_normals_accum_cb, &data, bm->totface >= BM_OMP_LIMIT);
-
- /* normalize the accumulated vertex normals */
- BM_iter_parallel(bm,
- BM_VERTS_OF_MESH,
- mesh_verts_calc_normals_normalize_cb,
- &data,
- bm->totvert >= BM_OMP_LIMIT);
-}
-
-static void mesh_faces_calc_normals_cb(void *UNUSED(userdata), MempoolIterData *mp_f)
-{
- BMFace *f = (BMFace *)mp_f;
-
- BM_face_normal_update(f);
-}
-
-/**
- * \brief BMesh Compute Normals
- *
- * Updates the normals of a mesh.
- */
-void BM_mesh_normals_update(BMesh *bm)
-{
- float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
-
- /* Parallel mempool iteration does not allow generating indices inline anymore... */
- BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE));
-
- /* calculate all face normals */
- BM_iter_parallel(
- bm, BM_FACES_OF_MESH, mesh_faces_calc_normals_cb, NULL, bm->totface >= BM_OMP_LIMIT);
-
- /* Zero out vertex normals */
- BMIter viter;
- BMVert *v;
- int i;
-
- BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
- BM_elem_index_set(v, i); /* set_inline */
- zero_v3(v->no);
- }
- bm->elem_index_dirty &= ~BM_VERT;
-
- /* Compute normalized direction vectors for each edge.
- * Directions will be used for calculating the weights of the face normals on the vertex normals.
- */
- bm_mesh_edges_calc_vectors(bm, edgevec, NULL);
-
- /* Add weighted face normals to vertices, and normalize vert normals. */
- bm_mesh_verts_calc_normals(bm, (const float(*)[3])edgevec, NULL, NULL, NULL);
- MEM_freeN(edgevec);
-}
-
-/**
- * \brief BMesh Compute Normals from/to external data.
- *
- * Computes the vertex normals of a mesh into vnos,
- * using given vertex coordinates (vcos) and polygon normals (fnos).
- */
-void BM_verts_calc_normal_vcos(BMesh *bm,
- const float (*fnos)[3],
- const float (*vcos)[3],
- float (*vnos)[3])
-{
- float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
-
- /* Compute normalized direction vectors for each edge.
- * Directions will be used for calculating the weights of the face normals on the vertex normals.
- */
- bm_mesh_edges_calc_vectors(bm, edgevec, vcos);
-
- /* Add weighted face normals to vertices, and normalize vert normals. */
- bm_mesh_verts_calc_normals(bm, (const float(*)[3])edgevec, fnos, vcos, vnos);
- MEM_freeN(edgevec);
-}
-
-/**
- * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normal_vcos
- */
-static void bm_mesh_edges_sharp_tag(BMesh *bm,
- const float (*vnos)[3],
- const float (*fnos)[3],
- float (*r_lnos)[3],
- const float split_angle,
- const bool do_sharp_edges_tag)
-{
- BMIter eiter;
- BMEdge *e;
- int i;
-
- const bool check_angle = (split_angle < (float)M_PI);
- const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f;
-
- {
- char htype = BM_VERT | BM_LOOP;
- if (fnos) {
- htype |= BM_FACE;
- }
- BM_mesh_elem_index_ensure(bm, htype);
- }
-
- /* This first loop checks which edges are actually smooth,
- * and pre-populate lnos with vnos (as if they were all smooth). */
- BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
- BMLoop *l_a, *l_b;
-
- BM_elem_index_set(e, i); /* set_inline */
- BM_elem_flag_disable(e, BM_ELEM_TAG); /* Clear tag (means edge is sharp). */
-
- /* An edge with only two loops, might be smooth... */
- if (BM_edge_loop_pair(e, &l_a, &l_b)) {
- bool is_angle_smooth = true;
- if (check_angle) {
- const float *no_a = fnos ? fnos[BM_elem_index_get(l_a->f)] : l_a->f->no;
- const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no;
- is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle_cos);
- }
-
- /* We only tag edges that are *really* smooth:
- * If the angle between both its polys' normals is below split_angle value,
- * and it is tagged as such,
- * and both its faces are smooth,
- * and both its faces have compatible (non-flipped) normals,
- * i.e. both loops on the same edge do not share the same vertex.
- */
- if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) &&
- BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
- if (is_angle_smooth) {
- const float *no;
- BM_elem_flag_enable(e, BM_ELEM_TAG);
-
- /* linked vertices might be fully smooth, copy their normals to loop ones. */
- if (r_lnos) {
- no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no;
- copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no);
- no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no;
- copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no);
- }
- }
- else if (do_sharp_edges_tag) {
- /* Note that we do not care about the other sharp-edge cases
- * (sharp poly, non-manifold edge, etc.),
- * only tag edge as sharp when it is due to angle threshold. */
- BM_elem_flag_disable(e, BM_ELEM_SMOOTH);
- }
- }
- }
- }
-
- bm->elem_index_dirty &= ~BM_EDGE;
-}
-
-/**
- * Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not.
- * Needed because cyclic smooth fans have no obvious 'entry point',
- * and yet we need to walk them once, and only once.
- */
-bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
-{
- BMLoop *lfan_pivot_next = l_curr;
- BMEdge *e_next = l_curr->e;
-
- BLI_assert(!BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG));
- BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
-
- while (true) {
- /* Much simpler than in sibling code with basic Mesh data! */
- lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot_next, &e_next);
-
- if (!lfan_pivot_next || !BM_elem_flag_test(e_next, BM_ELEM_TAG)) {
- /* Sharp loop/edge, so not a cyclic smooth fan... */
- return false;
- }
- /* Smooth loop/edge... */
- if (BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG)) {
- if (lfan_pivot_next == l_curr) {
- /* We walked around a whole cyclic smooth fan
- * without finding any already-processed loop,
- * means we can use initial l_curr/l_prev edge as start for this smooth fan. */
- return true;
- }
- /* ... already checked in some previous looping, we can abort. */
- return false;
- }
- /* ... we can skip it in future, and keep checking the smooth fan. */
- BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
- }
-}
-
-/**
- * BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c
- * Will use first clnors_data array, and fallback to cd_loop_clnors_offset
- * (use NULL and -1 to not use clnors).
- *
- * \note This sets #BM_ELEM_TAG which is used in tool code (e.g. T84426).
- * we could add a low-level API flag for this, see #BM_ELEM_API_FLAG_ENABLE and friends.
- */
-static void bm_mesh_loops_calc_normals(BMesh *bm,
- const float (*vcos)[3],
- const float (*fnos)[3],
- float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- const short (*clnors_data)[2],
- const int cd_loop_clnors_offset,
- const bool do_rebuild)
-{
- BMIter fiter;
- BMFace *f_curr;
- const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
-
- MLoopNorSpaceArray _lnors_spacearr = {NULL};
-
- /* Temp normal stack. */
- BLI_SMALLSTACK_DECLARE(normal, float *);
- /* Temp clnors stack. */
- BLI_SMALLSTACK_DECLARE(clnors, short *);
- /* Temp edge vectors stack, only used when computing lnor spacearr. */
- BLI_Stack *edge_vectors = NULL;
-
- {
- char htype = 0;
- if (vcos) {
- htype |= BM_VERT;
- }
- /* Face/Loop indices are set inline below. */
- BM_mesh_elem_index_ensure(bm, htype);
- }
-
- if (!r_lnors_spacearr && has_clnors) {
- /* We need to compute lnor spacearr if some custom lnor data are given to us! */
- r_lnors_spacearr = &_lnors_spacearr;
- }
- if (r_lnors_spacearr) {
- BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
- edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
- }
-
- /* Clear all loops' tags (means none are to be skipped for now). */
- int index_face, index_loop = 0;
- BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) {
- BMLoop *l_curr, *l_first;
-
- BM_elem_index_set(f_curr, index_face); /* set_inline */
-
- l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
- do {
- BM_elem_index_set(l_curr, index_loop++); /* set_inline */
- BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
- } while ((l_curr = l_curr->next) != l_first);
- }
- bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
-
- /* We now know edges that can be smoothed (they are tagged),
- * and edges that will be hard (they aren't).
- * Now, time to generate the normals.
- */
- BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
- BMLoop *l_curr, *l_first;
-
- l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
- do {
- if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
- !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) {
- continue;
- }
- /* A smooth edge, we have to check for cyclic smooth fan case.
- * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge
- * as 'entry point', otherwise we can skip it. */
-
- /* Note: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store
- * mlfan_pivot's in a stack, to avoid having to fan again around
- * the vert during actual computation of clnor & clnorspace. However, this would complicate
- * the code, add more memory usage, and
- * BM_vert_step_fan_loop() is quite cheap in term of CPU cycles,
- * so really think it's not worth it. */
- if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
- (BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !BM_loop_check_cyclic_smooth_fan(l_curr))) {
- }
- else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
- !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) {
- /* Simple case (both edges around that vertex are sharp in related polygon),
- * this vertex just takes its poly normal.
- */
- const int l_curr_index = BM_elem_index_get(l_curr);
- const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no;
- copy_v3_v3(r_lnos[l_curr_index], no);
-
- /* If needed, generate this (simple!) lnor space. */
- if (r_lnors_spacearr) {
- float vec_curr[3], vec_prev[3];
- MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr);
-
- {
- const BMVert *v_pivot = l_curr->v;
- const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
- const BMVert *v_1 = BM_edge_other_vert(l_curr->e, v_pivot);
- const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co;
- const BMVert *v_2 = BM_edge_other_vert(l_curr->prev->e, v_pivot);
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
-
- sub_v3_v3v3(vec_curr, co_1, co_pivot);
- normalize_v3(vec_curr);
- sub_v3_v3v3(vec_prev, co_2, co_pivot);
- normalize_v3(vec_prev);
- }
-
- BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL);
- /* We know there is only one loop in this space,
- * no need to create a linklist in this case... */
- BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true);
-
- if (has_clnors) {
- const short(*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] :
- (const void *)BM_ELEM_CD_GET_VOID_P(
- l_curr, cd_loop_clnors_offset);
- BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]);
- }
- }
- }
- /* We *do not need* to check/tag loops as already computed!
- * Due to the fact a loop only links to one of its two edges,
- * a same fan *will never be walked more than once!*
- * Since we consider edges having neighbor faces with inverted (flipped) normals as sharp,
- * we are sure that no fan will be skipped, even only considering the case
- * (sharp curr_edge, smooth prev_edge), and not the alternative
- * (smooth curr_edge, sharp prev_edge).
- * All this due/thanks to link between normals and loop ordering.
- */
- else {
- /* We have to fan around current vertex, until we find the other non-smooth edge,
- * and accumulate face normals into the vertex!
- * Note in case this vertex has only one sharp edge,
- * this is a waste because the normal is the same as the vertex normal,
- * but I do not see any easy way to detect that (would need to count number of sharp edges
- * per vertex, I doubt the additional memory usage would be worth it, especially as it
- * should not be a common case in real-life meshes anyway).
- */
- BMVert *v_pivot = l_curr->v;
- BMEdge *e_next;
- const BMEdge *e_org = l_curr->e;
- BMLoop *lfan_pivot, *lfan_pivot_next;
- int lfan_pivot_index;
- float lnor[3] = {0.0f, 0.0f, 0.0f};
- float vec_curr[3], vec_next[3], vec_org[3];
-
- /* We validate clnors data on the fly - cheapest way to do! */
- int clnors_avg[2] = {0, 0};
- const short(*clnor_ref)[2] = NULL;
- int clnors_nbr = 0;
- bool clnors_invalid = false;
-
- const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
-
- MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) :
- NULL;
-
- BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors));
-
- lfan_pivot = l_curr;
- lfan_pivot_index = BM_elem_index_get(lfan_pivot);
- e_next = lfan_pivot->e; /* Current edge here, actually! */
-
- /* Only need to compute previous edge's vector once,
- * then we can just reuse old current one! */
- {
- const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
-
- sub_v3_v3v3(vec_org, co_2, co_pivot);
- normalize_v3(vec_org);
- copy_v3_v3(vec_curr, vec_org);
-
- if (r_lnors_spacearr) {
- BLI_stack_push(edge_vectors, vec_org);
- }
- }
-
- while (true) {
- /* Much simpler than in sibling code with basic Mesh data! */
- lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
- if (lfan_pivot_next) {
- BLI_assert(lfan_pivot_next->v == v_pivot);
- }
- else {
- /* next edge is non-manifold, we have to find it ourselves! */
- e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
- }
-
- /* Compute edge vector.
- * NOTE: We could pre-compute those into an array, in the first iteration,
- * instead of computing them twice (or more) here.
- * However, time gained is not worth memory and time lost,
- * given the fact that this code should not be called that much in real-life meshes.
- */
- {
- const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
-
- sub_v3_v3v3(vec_next, co_2, co_pivot);
- normalize_v3(vec_next);
- }
-
- {
- /* Code similar to accumulate_vertex_normals_poly_v3. */
- /* Calculate angle between the two poly edges incident on this vertex. */
- const BMFace *f = lfan_pivot->f;
- const float fac = saacos(dot_v3v3(vec_next, vec_curr));
- const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
- /* Accumulate */
- madd_v3_v3fl(lnor, no, fac);
-
- if (has_clnors) {
- /* Accumulate all clnors, if they are not all equal we have to fix that! */
- const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] :
- (const void *)BM_ELEM_CD_GET_VOID_P(
- lfan_pivot, cd_loop_clnors_offset);
- if (clnors_nbr) {
- clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] ||
- (*clnor_ref)[1] != (*clnor)[1]);
- }
- else {
- clnor_ref = clnor;
- }
- clnors_avg[0] += (*clnor)[0];
- clnors_avg[1] += (*clnor)[1];
- clnors_nbr++;
- /* We store here a pointer to all custom lnors processed. */
- BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
- }
- }
-
- /* We store here a pointer to all loop-normals processed. */
- BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]);
-
- if (r_lnors_spacearr) {
- /* Assign current lnor space to current 'vertex' loop. */
- BKE_lnor_space_add_loop(
- r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false);
- if (e_next != e_org) {
- /* We store here all edges-normalized vectors processed. */
- BLI_stack_push(edge_vectors, vec_next);
- }
- }
-
- if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
- /* Next edge is sharp, we have finished with this fan of faces around this vert! */
- break;
- }
-
- /* Copy next edge vector to current one. */
- copy_v3_v3(vec_curr, vec_next);
- /* Next pivot loop to current one. */
- lfan_pivot = lfan_pivot_next;
- lfan_pivot_index = BM_elem_index_get(lfan_pivot);
- }
-
- {
- float lnor_len = normalize_v3(lnor);
-
- /* If we are generating lnor spacearr, we can now define the one for this fan. */
- if (r_lnors_spacearr) {
- if (UNLIKELY(lnor_len == 0.0f)) {
- /* Use vertex normal as fallback! */
- copy_v3_v3(lnor, r_lnos[lfan_pivot_index]);
- lnor_len = 1.0f;
- }
-
- BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors);
-
- if (has_clnors) {
- if (clnors_invalid) {
- short *clnor;
-
- clnors_avg[0] /= clnors_nbr;
- clnors_avg[1] /= clnors_nbr;
- /* Fix/update all clnors of this fan with computed average value. */
-
- /* Prints continuously when merge custom normals, so commenting. */
- /* printf("Invalid clnors in this fan!\n"); */
-
- while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
- // print_v2("org clnor", clnor);
- clnor[0] = (short)clnors_avg[0];
- clnor[1] = (short)clnors_avg[1];
- }
- // print_v2("new clnors", clnors_avg);
- }
- else {
- /* We still have to consume the stack! */
- while (BLI_SMALLSTACK_POP(clnors)) {
- /* pass */
- }
- }
- BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor);
- }
- }
-
- /* In case we get a zero normal here, just use vertex normal already set! */
- if (LIKELY(lnor_len != 0.0f)) {
- /* Copy back the final computed normal into all related loop-normals. */
- float *nor;
-
- while ((nor = BLI_SMALLSTACK_POP(normal))) {
- copy_v3_v3(nor, lnor);
- }
- }
- else {
- /* We still have to consume the stack! */
- while (BLI_SMALLSTACK_POP(normal)) {
- /* pass */
- }
- }
- }
-
- /* Tag related vertex as sharp, to avoid fanning around it again
- * (in case it was a smooth one). */
- if (r_lnors_spacearr) {
- BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG);
- }
- }
- } while ((l_curr = l_curr->next) != l_first);
- }
-
- if (r_lnors_spacearr) {
- BLI_stack_free(edge_vectors);
- if (r_lnors_spacearr == &_lnors_spacearr) {
- BKE_lnor_spacearr_free(r_lnors_spacearr);
- }
- }
-}
-
-/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
-#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
-
-/**
- * Check each current smooth fan (one lnor space per smooth fan!), and if all its
- * matching custom lnors are not (enough) equal, add sharp edges as needed.
- */
-static bool bm_mesh_loops_split_lnor_fans(BMesh *bm,
- MLoopNorSpaceArray *lnors_spacearr,
- const float (*new_lnors)[3])
-{
- BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
- bool changed = false;
-
- BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
-
- for (int i = 0; i < bm->totloop; i++) {
- if (!lnors_spacearr->lspacearr[i]) {
- /* This should not happen in theory, but in some rare case (probably ugly geometry)
- * we can get some NULL loopspacearr at this point. :/
- * Maybe we should set those loops' edges as sharp?
- */
- BLI_BITMAP_ENABLE(done_loops, i);
- if (G.debug & G_DEBUG) {
- printf("WARNING! Getting invalid NULL loop space for loop %d!\n", i);
- }
- continue;
- }
-
- if (!BLI_BITMAP_TEST(done_loops, i)) {
- /* Notes:
- * * In case of mono-loop smooth fan, we have nothing to do.
- * * Loops in this linklist are ordered (in reversed order compared to how they were
- * discovered by BKE_mesh_normals_loop_split(), but this is not a problem).
- * Which means if we find a mismatching clnor,
- * we know all remaining loops will have to be in a new, different smooth fan/lnor space.
- * * In smooth fan case, we compare each clnor against a ref one,
- * to avoid small differences adding up into a real big one in the end!
- */
- if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
- BLI_BITMAP_ENABLE(done_loops, i);
- continue;
- }
-
- LinkNode *loops = lnors_spacearr->lspacearr[i]->loops;
- BMLoop *prev_ml = NULL;
- const float *org_nor = NULL;
-
- while (loops) {
- BMLoop *ml = loops->link;
- const int lidx = BM_elem_index_get(ml);
- const float *nor = new_lnors[lidx];
-
- if (!org_nor) {
- org_nor = nor;
- }
- else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
- /* Current normal differs too much from org one, we have to tag the edge between
- * previous loop's face and current's one as sharp.
- * We know those two loops do not point to the same edge,
- * since we do not allow reversed winding in a same smooth fan.
- */
- BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e;
-
- BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH);
- changed = true;
-
- org_nor = nor;
- }
-
- prev_ml = ml;
- loops = loops->next;
- BLI_BITMAP_ENABLE(done_loops, lidx);
- }
-
- /* We also have to check between last and first loops,
- * otherwise we may miss some sharp edges here!
- * This is just a simplified version of above while loop.
- * See T45984. */
- loops = lnors_spacearr->lspacearr[i]->loops;
- if (loops && org_nor) {
- BMLoop *ml = loops->link;
- const int lidx = BM_elem_index_get(ml);
- const float *nor = new_lnors[lidx];
-
- if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
- BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e;
-
- BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH);
- changed = true;
- }
- }
- }
- }
-
- MEM_freeN(done_loops);
- return changed;
-}
-
-/**
- * Assign custom normal data from given normal vectors, averaging normals
- * from one smooth fan as necessary.
- */
-static void bm_mesh_loops_assign_normal_data(BMesh *bm,
- MLoopNorSpaceArray *lnors_spacearr,
- short (*r_clnors_data)[2],
- const int cd_loop_clnors_offset,
- const float (*new_lnors)[3])
-{
- BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
-
- BLI_SMALLSTACK_DECLARE(clnors_data, short *);
-
- BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
-
- for (int i = 0; i < bm->totloop; i++) {
- if (!lnors_spacearr->lspacearr[i]) {
- BLI_BITMAP_ENABLE(done_loops, i);
- if (G.debug & G_DEBUG) {
- printf("WARNING! Still getting invalid NULL loop space in second loop for loop %d!\n", i);
- }
- continue;
- }
-
- if (!BLI_BITMAP_TEST(done_loops, i)) {
- /* Note we accumulate and average all custom normals in current smooth fan,
- * to avoid getting different clnors data (tiny differences in plain custom normals can
- * give rather huge differences in computed 2D factors).
- */
- LinkNode *loops = lnors_spacearr->lspacearr[i]->loops;
-
- if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
- BMLoop *ml = (BMLoop *)loops;
- const int lidx = BM_elem_index_get(ml);
-
- BLI_assert(lidx == i);
-
- const float *nor = new_lnors[lidx];
- short *clnor = r_clnors_data ? &r_clnors_data[lidx] :
- BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset);
-
- BKE_lnor_space_custom_normal_to_data(lnors_spacearr->lspacearr[i], nor, clnor);
- BLI_BITMAP_ENABLE(done_loops, i);
- }
- else {
- int nbr_nors = 0;
- float avg_nor[3];
- short clnor_data_tmp[2], *clnor_data;
-
- zero_v3(avg_nor);
-
- while (loops) {
- BMLoop *ml = loops->link;
- const int lidx = BM_elem_index_get(ml);
- const float *nor = new_lnors[lidx];
- short *clnor = r_clnors_data ? &r_clnors_data[lidx] :
- BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset);
-
- nbr_nors++;
- add_v3_v3(avg_nor, nor);
- BLI_SMALLSTACK_PUSH(clnors_data, clnor);
-
- loops = loops->next;
- BLI_BITMAP_ENABLE(done_loops, lidx);
- }
-
- mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors);
- BKE_lnor_space_custom_normal_to_data(
- lnors_spacearr->lspacearr[i], avg_nor, clnor_data_tmp);
-
- while ((clnor_data = BLI_SMALLSTACK_POP(clnors_data))) {
- clnor_data[0] = clnor_data_tmp[0];
- clnor_data[1] = clnor_data_tmp[1];
- }
- }
- }
- }
-
- MEM_freeN(done_loops);
-}
-
-/**
- * Compute internal representation of given custom normals (as an array of float[2] or data layer).
- *
- * It also makes sure the mesh matches those custom normals, by marking new sharp edges to split
- * the smooth fans when loop normals for the same vertex are different, or averaging the normals
- * instead, depending on the do_split_fans parameter.
- */
-static void bm_mesh_loops_custom_normals_set(BMesh *bm,
- const float (*vcos)[3],
- const float (*vnos)[3],
- const float (*fnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- short (*r_clnors_data)[2],
- const int cd_loop_clnors_offset,
- float (*new_lnors)[3],
- const int cd_new_lnors_offset,
- bool do_split_fans)
-{
- BMFace *f;
- BMLoop *l;
- BMIter liter, fiter;
- float(*cur_lnors)[3] = MEM_mallocN(sizeof(*cur_lnors) * bm->totloop, __func__);
-
- BKE_lnor_spacearr_clear(r_lnors_spacearr);
-
- /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
- * When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, vnos, fnos, cur_lnors, (float)M_PI, false);
-
- /* Finish computing lnos by accumulating face normals
- * in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
-
- /* Extract new normals from the data layer if necessary. */
- float(*custom_lnors)[3] = new_lnors;
-
- if (new_lnors == NULL) {
- custom_lnors = MEM_mallocN(sizeof(*new_lnors) * bm->totloop, __func__);
-
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- const float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_new_lnors_offset);
- copy_v3_v3(custom_lnors[BM_elem_index_get(l)], normal);
- }
- }
- }
-
- /* Validate the new normals. */
- for (int i = 0; i < bm->totloop; i++) {
- if (is_zero_v3(custom_lnors[i])) {
- copy_v3_v3(custom_lnors[i], cur_lnors[i]);
- }
- else {
- normalize_v3(custom_lnors[i]);
- }
- }
-
- /* Now, check each current smooth fan (one lnor space per smooth fan!),
- * and if all its matching custom lnors are not equal, add sharp edges as needed. */
- if (do_split_fans && bm_mesh_loops_split_lnor_fans(bm, r_lnors_spacearr, custom_lnors)) {
- /* If any sharp edges were added, run bm_mesh_loops_calc_normals() again to get lnor
- * spacearr/smooth fans matching the given custom lnors. */
- BKE_lnor_spacearr_clear(r_lnors_spacearr);
-
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
- }
-
- /* And we just have to convert plain object-space custom normals to our
- * lnor space-encoded ones. */
- bm_mesh_loops_assign_normal_data(
- bm, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, custom_lnors);
-
- MEM_freeN(cur_lnors);
-
- if (custom_lnors != new_lnors) {
- MEM_freeN(custom_lnors);
- }
-}
-
-static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm,
- const float (*vnos)[3],
- const float (*fnos)[3],
- float (*r_lnos)[3])
-{
- BMIter fiter;
- BMFace *f_curr;
-
- {
- char htype = BM_LOOP;
- if (vnos) {
- htype |= BM_VERT;
- }
- if (fnos) {
- htype |= BM_FACE;
- }
- BM_mesh_elem_index_ensure(bm, htype);
- }
-
- BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
- BMLoop *l_curr, *l_first;
- const bool is_face_flat = !BM_elem_flag_test(f_curr, BM_ELEM_SMOOTH);
-
- l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
- do {
- const float *no = is_face_flat ? (fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no) :
- (vnos ? vnos[BM_elem_index_get(l_curr->v)] : l_curr->v->no);
- copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no);
-
- } while ((l_curr = l_curr->next) != l_first);
- }
-}
-
-#if 0 /* Unused currently */
-/**
- * \brief BMesh Compute Loop Normals
- *
- * Updates the loop normals of a mesh.
- * Assumes vertex and face normals are valid (else call BM_mesh_normals_update() first)!
- */
-void BM_mesh_loop_normals_update(BMesh *bm,
- const bool use_split_normals,
- const float split_angle,
- float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- const short (*clnors_data)[2],
- const int cd_loop_clnors_offset)
-{
- const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
-
- if (use_split_normals) {
- /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
- * When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos);
-
- /* Finish computing lnos by accumulating face normals
- * in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
- }
- else {
- BLI_assert(!r_lnors_spacearr);
- bm_mesh_loops_calc_normals_no_autosmooth(bm, NULL, NULL, r_lnos);
- }
-}
-#endif
-
-/**
- * \brief BMesh Compute Loop Normals from/to external data.
- *
- * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
- * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry
- * (splitting edges).
- */
-void BM_loops_calc_normal_vcos(BMesh *bm,
- const float (*vcos)[3],
- const float (*vnos)[3],
- const float (*fnos)[3],
- const bool use_split_normals,
- const float split_angle,
- float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- short (*clnors_data)[2],
- const int cd_loop_clnors_offset,
- const bool do_rebuild)
-{
- const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
-
- if (use_split_normals) {
- /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
- * When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false);
-
- /* Finish computing lnos by accumulating face normals
- * in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild);
- }
- else {
- BLI_assert(!r_lnors_spacearr);
- bm_mesh_loops_calc_normals_no_autosmooth(bm, vnos, fnos, r_lnos);
- }
-}
-
-/**
- * Define sharp edges as needed to mimic 'autosmooth' from angle threshold.
- *
- * Used when defining an empty custom loop normals data layer,
- * to keep same shading as with autosmooth!
- */
-void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
-{
- if (split_angle >= (float)M_PI) {
- /* Nothing to do! */
- return;
- }
-
- bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true);
-}
-
-void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3])
-{
- BLI_assert(bm->lnor_spacearr != NULL);
-
- if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
- BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
- }
-
- int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
-
- BM_loops_calc_normal_vcos(bm,
- NULL,
- NULL,
- NULL,
- true,
- M_PI,
- r_lnors,
- bm->lnor_spacearr,
- NULL,
- cd_loop_clnors_offset,
- false);
- bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
-}
-
-#define CLEAR_SPACEARRAY_THRESHOLD(x) ((x) / 2)
-
-void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
-{
- if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
- return;
- }
- if (do_invalidate_all || bm->totvertsel > CLEAR_SPACEARRAY_THRESHOLD(bm->totvert)) {
- bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
- return;
- }
- if (bm->lnor_spacearr == NULL) {
- bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
- return;
- }
-
- BMVert *v;
- BMLoop *l;
- BMIter viter, liter;
- /* Note: we could use temp tag of BMItem for that,
- * but probably better not use it in such a low-level func?
- * --mont29 */
- BLI_bitmap *done_verts = BLI_BITMAP_NEW(bm->totvert, __func__);
-
- BM_mesh_elem_index_ensure(bm, BM_VERT);
-
- /* When we affect a given vertex, we may affect following smooth fans:
- * - all smooth fans of said vertex;
- * - all smooth fans of all immediate loop-neighbors vertices;
- * This can be simplified as 'all loops of selected vertices and their immediate neighbors'
- * need to be tagged for update.
- */
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
- BM_ELEM_API_FLAG_ENABLE(l, BM_LNORSPACE_UPDATE);
-
- /* Note that we only handle unselected neighbor vertices here, main loop will take care of
- * selected ones. */
- if ((!BM_elem_flag_test(l->prev->v, BM_ELEM_SELECT)) &&
- !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->prev->v))) {
-
- BMLoop *l_prev;
- BMIter liter_prev;
- BM_ITER_ELEM (l_prev, &liter_prev, l->prev->v, BM_LOOPS_OF_VERT) {
- BM_ELEM_API_FLAG_ENABLE(l_prev, BM_LNORSPACE_UPDATE);
- }
- BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_prev->v));
- }
-
- if ((!BM_elem_flag_test(l->next->v, BM_ELEM_SELECT)) &&
- !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->next->v))) {
-
- BMLoop *l_next;
- BMIter liter_next;
- BM_ITER_ELEM (l_next, &liter_next, l->next->v, BM_LOOPS_OF_VERT) {
- BM_ELEM_API_FLAG_ENABLE(l_next, BM_LNORSPACE_UPDATE);
- }
- BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_next->v));
- }
- }
-
- BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(v));
- }
- }
-
- MEM_freeN(done_verts);
- bm->spacearr_dirty |= BM_SPACEARR_DIRTY;
-}
-
-void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
-{
- BLI_assert(bm->lnor_spacearr != NULL);
-
- if (!(bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL))) {
- return;
- }
- BMFace *f;
- BMLoop *l;
- BMIter fiter, liter;
-
- float(*r_lnors)[3] = MEM_callocN(sizeof(*r_lnors) * bm->totloop, __func__);
- float(*oldnors)[3] = preserve_clnor ? MEM_mallocN(sizeof(*oldnors) * bm->totloop, __func__) :
- NULL;
-
- int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
-
- BM_mesh_elem_index_ensure(bm, BM_LOOP);
-
- if (preserve_clnor) {
- BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
-
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) ||
- bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
- short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
- int l_index = BM_elem_index_get(l);
-
- BKE_lnor_space_custom_data_to_normal(
- bm->lnor_spacearr->lspacearr[l_index], *clnor, oldnors[l_index]);
- }
- }
- }
- }
-
- if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
- BKE_lnor_spacearr_clear(bm->lnor_spacearr);
- }
- BM_loops_calc_normal_vcos(bm,
- NULL,
- NULL,
- NULL,
- true,
- M_PI,
- r_lnors,
- bm->lnor_spacearr,
- NULL,
- cd_loop_clnors_offset,
- true);
- MEM_freeN(r_lnors);
-
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) ||
- bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
- if (preserve_clnor) {
- short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
- int l_index = BM_elem_index_get(l);
- BKE_lnor_space_custom_normal_to_data(
- bm->lnor_spacearr->lspacearr[l_index], oldnors[l_index], *clnor);
- }
- BM_ELEM_API_FLAG_DISABLE(l, BM_LNORSPACE_UPDATE);
- }
- }
- }
-
- MEM_SAFE_FREE(oldnors);
- bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
-
-#ifndef NDEBUG
- BM_lnorspace_err(bm);
-#endif
-}
-
-/**
- * \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
- * take care to run this before setting up tags.
- */
-void BM_lnorspace_update(BMesh *bm)
-{
- if (bm->lnor_spacearr == NULL) {
- bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
- }
- if (bm->lnor_spacearr->lspacearr == NULL) {
- float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
-
- BM_lnorspacearr_store(bm, lnors);
-
- MEM_freeN(lnors);
- }
- else if (bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)) {
- BM_lnorspace_rebuild(bm, false);
- }
-}
-
-void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
-{
- BMFace *f;
- BMEdge *e;
- BMIter fiter, eiter;
- BMLoop *l_curr, *l_first;
-
- if (do_edges) {
- int index_edge;
- BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, index_edge) {
- BMLoop *l_a, *l_b;
-
- BM_elem_index_set(e, index_edge); /* set_inline */
- BM_elem_flag_disable(e, BM_ELEM_TAG);
- if (BM_edge_loop_pair(e, &l_a, &l_b)) {
- if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
- BM_elem_flag_enable(e, BM_ELEM_TAG);
- }
- }
- }
- bm->elem_index_dirty &= ~BM_EDGE;
- }
-
- int index_face, index_loop = 0;
- BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, index_face) {
- BM_elem_index_set(f, index_face); /* set_inline */
- l_curr = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- BM_elem_index_set(l_curr, index_loop++); /* set_inline */
- BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
- } while ((l_curr = l_curr->next) != l_first);
- }
- bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
-}
-
-/**
- * Auxiliary function only used by rebuild to detect if any spaces were not marked as invalid.
- * Reports error if any of the lnor spaces change after rebuilding, meaning that all the possible
- * lnor spaces to be rebuilt were not correctly marked.
- */
-#ifndef NDEBUG
-void BM_lnorspace_err(BMesh *bm)
-{
- bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
- bool clear = true;
-
- MLoopNorSpaceArray *temp = MEM_callocN(sizeof(*temp), __func__);
- temp->lspacearr = NULL;
-
- BKE_lnor_spacearr_init(temp, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
-
- int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
- float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
- BM_loops_calc_normal_vcos(
- bm, NULL, NULL, NULL, true, M_PI, lnors, temp, NULL, cd_loop_clnors_offset, true);
-
- for (int i = 0; i < bm->totloop; i++) {
- int j = 0;
- j += compare_ff(
- temp->lspacearr[i]->ref_alpha, bm->lnor_spacearr->lspacearr[i]->ref_alpha, 1e-4f);
- j += compare_ff(
- temp->lspacearr[i]->ref_beta, bm->lnor_spacearr->lspacearr[i]->ref_beta, 1e-4f);
- j += compare_v3v3(
- temp->lspacearr[i]->vec_lnor, bm->lnor_spacearr->lspacearr[i]->vec_lnor, 1e-4f);
- j += compare_v3v3(
- temp->lspacearr[i]->vec_ortho, bm->lnor_spacearr->lspacearr[i]->vec_ortho, 1e-4f);
- j += compare_v3v3(
- temp->lspacearr[i]->vec_ref, bm->lnor_spacearr->lspacearr[i]->vec_ref, 1e-4f);
-
- if (j != 5) {
- clear = false;
- break;
- }
- }
- BKE_lnor_spacearr_free(temp);
- MEM_freeN(temp);
- MEM_freeN(lnors);
- BLI_assert(clear);
-
- bm->spacearr_dirty &= ~BM_SPACEARR_DIRTY_ALL;
-}
-#endif
-
-static void bm_loop_normal_mark_indiv_do_loop(BMLoop *l,
- BLI_bitmap *loops,
- MLoopNorSpaceArray *lnor_spacearr,
- int *totloopsel,
- const bool do_all_loops_of_vert)
-{
- if (l != NULL) {
- const int l_idx = BM_elem_index_get(l);
-
- if (!BLI_BITMAP_TEST(loops, l_idx)) {
- /* If vert and face selected share a loop, mark it for editing. */
- BLI_BITMAP_ENABLE(loops, l_idx);
- (*totloopsel)++;
-
- if (do_all_loops_of_vert) {
- /* If required, also mark all loops shared by that vertex.
- * This is needed when loop spaces may change
- * (i.e. when some faces or edges might change of smooth/sharp status). */
- BMIter liter;
- BMLoop *lfan;
- BM_ITER_ELEM (lfan, &liter, l->v, BM_LOOPS_OF_VERT) {
- const int lfan_idx = BM_elem_index_get(lfan);
- if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
- BLI_BITMAP_ENABLE(loops, lfan_idx);
- (*totloopsel)++;
- }
- }
- }
- else {
- /* Mark all loops in same loop normal space (aka smooth fan). */
- if ((lnor_spacearr->lspacearr[l_idx]->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
- for (LinkNode *node = lnor_spacearr->lspacearr[l_idx]->loops; node; node = node->next) {
- const int lfan_idx = BM_elem_index_get((BMLoop *)node->link);
- if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
- BLI_BITMAP_ENABLE(loops, lfan_idx);
- (*totloopsel)++;
- }
- }
- }
- }
- }
- }
-}
-
-/* Mark the individual clnors to be edited, if multiple selection methods are used. */
-static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops, const bool do_all_loops_of_vert)
-{
- BMEditSelection *ese, *ese_prev;
- int totloopsel = 0;
-
- const bool sel_verts = (bm->selectmode & SCE_SELECT_VERTEX) != 0;
- const bool sel_edges = (bm->selectmode & SCE_SELECT_EDGE) != 0;
- const bool sel_faces = (bm->selectmode & SCE_SELECT_FACE) != 0;
- const bool use_sel_face_history = sel_faces && (sel_edges || sel_verts);
-
- BM_mesh_elem_index_ensure(bm, BM_LOOP);
-
- BLI_assert(bm->lnor_spacearr != NULL);
- BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
-
- if (use_sel_face_history) {
- /* Using face history allows to select a single loop from a single face...
- * Note that this is On² piece of code,
- * but it is not designed to be used with huge selection sets,
- * rather with only a few items selected at most.*/
- /* Goes from last selected to the first selected element. */
- for (ese = bm->selected.last; ese; ese = ese->prev) {
- if (ese->htype == BM_FACE) {
- /* If current face is selected,
- * then any verts to be edited must have been selected before it. */
- for (ese_prev = ese->prev; ese_prev; ese_prev = ese_prev->prev) {
- if (ese_prev->htype == BM_VERT) {
- bm_loop_normal_mark_indiv_do_loop(
- BM_face_vert_share_loop((BMFace *)ese->ele, (BMVert *)ese_prev->ele),
- loops,
- bm->lnor_spacearr,
- &totloopsel,
- do_all_loops_of_vert);
- }
- else if (ese_prev->htype == BM_EDGE) {
- BMEdge *e = (BMEdge *)ese_prev->ele;
- bm_loop_normal_mark_indiv_do_loop(BM_face_vert_share_loop((BMFace *)ese->ele, e->v1),
- loops,
- bm->lnor_spacearr,
- &totloopsel,
- do_all_loops_of_vert);
-
- bm_loop_normal_mark_indiv_do_loop(BM_face_vert_share_loop((BMFace *)ese->ele, e->v2),
- loops,
- bm->lnor_spacearr,
- &totloopsel,
- do_all_loops_of_vert);
- }
- }
- }
- }
- }
- else {
- if (sel_faces) {
- /* Only select all loops of selected faces. */
- BMLoop *l;
- BMFace *f;
- BMIter liter, fiter;
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- bm_loop_normal_mark_indiv_do_loop(
- l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- }
- }
- }
- }
- if (sel_edges) {
- /* Only select all loops of selected edges. */
- BMLoop *l;
- BMEdge *e;
- BMIter liter, eiter;
- BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
- if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
- bm_loop_normal_mark_indiv_do_loop(
- l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- /* Loops actually 'have' two edges, or said otherwise, a selected edge actually selects
- * *two* loops in each of its faces. We have to find the other one too. */
- if (BM_vert_in_edge(e, l->next->v)) {
- bm_loop_normal_mark_indiv_do_loop(
- l->next, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- }
- else {
- BLI_assert(BM_vert_in_edge(e, l->prev->v));
- bm_loop_normal_mark_indiv_do_loop(
- l->prev, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- }
- }
- }
- }
- }
- if (sel_verts) {
- /* Select all loops of selected verts. */
- BMLoop *l;
- BMVert *v;
- BMIter liter, viter;
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
- bm_loop_normal_mark_indiv_do_loop(
- l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- }
- }
- }
- }
- }
-
- return totloopsel;
-}
-
-static void loop_normal_editdata_init(
- BMesh *bm, BMLoopNorEditData *lnor_ed, BMVert *v, BMLoop *l, const int offset)
-{
- BLI_assert(bm->lnor_spacearr != NULL);
- BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
-
- const int l_index = BM_elem_index_get(l);
- short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, offset);
-
- lnor_ed->loop_index = l_index;
- lnor_ed->loop = l;
-
- float custom_normal[3];
- BKE_lnor_space_custom_data_to_normal(
- bm->lnor_spacearr->lspacearr[l_index], clnors_data, custom_normal);
-
- lnor_ed->clnors_data = clnors_data;
- copy_v3_v3(lnor_ed->nloc, custom_normal);
- copy_v3_v3(lnor_ed->niloc, custom_normal);
-
- lnor_ed->loc = v->co;
-}
-
-BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
- const bool do_all_loops_of_vert)
-{
- BMLoop *l;
- BMVert *v;
- BMIter liter, viter;
-
- int totloopsel = 0;
-
- BLI_assert(bm->spacearr_dirty == 0);
-
- BMLoopNorEditDataArray *lnors_ed_arr = MEM_callocN(sizeof(*lnors_ed_arr), __func__);
- lnors_ed_arr->lidx_to_lnor_editdata = MEM_callocN(
- sizeof(*lnors_ed_arr->lidx_to_lnor_editdata) * bm->totloop, __func__);
-
- if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
- BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
- }
- const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
-
- BM_mesh_elem_index_ensure(bm, BM_LOOP);
-
- BLI_bitmap *loops = BLI_BITMAP_NEW(bm->totloop, __func__);
-
- /* This function define loop normals to edit, based on selection modes and history. */
- totloopsel = bm_loop_normal_mark_indiv(bm, loops, do_all_loops_of_vert);
-
- if (totloopsel) {
- BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(
- sizeof(*lnor_ed) * totloopsel, __func__);
-
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
- BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
- if (BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
- loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
- lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
- lnor_ed++;
- }
- }
- }
- lnors_ed_arr->totloop = totloopsel;
- }
-
- MEM_freeN(loops);
- lnors_ed_arr->cd_custom_normal_offset = cd_custom_normal_offset;
- return lnors_ed_arr;
-}
-
-void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
-{
- MEM_SAFE_FREE(lnors_ed_arr->lnor_editdata);
- MEM_SAFE_FREE(lnors_ed_arr->lidx_to_lnor_editdata);
- MEM_freeN(lnors_ed_arr);
-}
-
-/**
- * \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
- * take care to run this before setting up tags.
- */
-bool BM_custom_loop_normals_to_vector_layer(BMesh *bm)
-{
- BMFace *f;
- BMLoop *l;
- BMIter liter, fiter;
-
- if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
- return false;
- }
-
- BM_lnorspace_update(bm);
- BM_mesh_elem_index_ensure(bm, BM_LOOP);
-
- /* Create a loop normal layer. */
- if (!CustomData_has_layer(&bm->ldata, CD_NORMAL)) {
- BM_data_layer_add(bm, &bm->ldata, CD_NORMAL);
-
- CustomData_set_layer_flag(&bm->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
- }
-
- const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
- const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL);
-
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- const int l_index = BM_elem_index_get(l);
- const short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, cd_custom_normal_offset);
- float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_normal_offset);
-
- BKE_lnor_space_custom_data_to_normal(
- bm->lnor_spacearr->lspacearr[l_index], clnors_data, normal);
- }
- }
-
- return true;
-}
-
-void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
-{
- if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL) ||
- !CustomData_has_layer(&bm->ldata, CD_NORMAL)) {
- return;
- }
-
- const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
- const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL);
-
- if (bm->lnor_spacearr == NULL) {
- bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
- }
-
- bm_mesh_loops_custom_normals_set(bm,
- NULL,
- NULL,
- NULL,
- bm->lnor_spacearr,
- NULL,
- cd_custom_normal_offset,
- NULL,
- cd_normal_offset,
- add_sharp_edges);
-
- bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
-}
-
-/**
* \brief BMesh Begin Edit
*
* Functions for setting up a mesh for editing and cleaning up after
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index c1c2f17d7c1..456275cf157 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -25,6 +25,7 @@
struct BMAllocTemplate;
struct BMLoopNorEditDataArray;
struct MLoopNorSpaceArray;
+struct BMPartialUpdate;
void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
@@ -40,43 +41,6 @@ void BM_mesh_free(BMesh *bm);
void BM_mesh_data_free(BMesh *bm);
void BM_mesh_clear(BMesh *bm);
-void BM_mesh_normals_update(BMesh *bm);
-void BM_verts_calc_normal_vcos(BMesh *bm,
- const float (*fnos)[3],
- const float (*vcos)[3],
- float (*vnos)[3]);
-void BM_loops_calc_normal_vcos(BMesh *bm,
- const float (*vcos)[3],
- const float (*vnos)[3],
- const float (*fnos)[3],
- const bool use_split_normals,
- const float split_angle,
- float (*r_lnos)[3],
- struct MLoopNorSpaceArray *r_lnors_spacearr,
- short (*clnors_data)[2],
- const int cd_loop_clnors_offset,
- const bool do_rebuild);
-
-bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr);
-void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3]);
-void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all);
-void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor);
-void BM_lnorspace_update(BMesh *bm);
-void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges);
-#ifndef NDEBUG
-void BM_lnorspace_err(BMesh *bm);
-#endif
-
-/* Loop Generics */
-struct BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
- const bool do_all_loops_of_vert);
-void BM_loop_normal_editdata_array_free(struct BMLoopNorEditDataArray *lnors_ed_arr);
-
-bool BM_custom_loop_normals_to_vector_layer(struct BMesh *bm);
-void BM_custom_loop_normals_from_vector_layer(struct BMesh *bm, bool add_sharp_edges);
-
-void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle);
-
void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag);
void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c
new file mode 100644
index 00000000000..a3eae6dabe8
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c
@@ -0,0 +1,1859 @@
+/*
+ * 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 bmesh
+ *
+ * BM mesh normal calculation functions.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_linklist_stack.h"
+#include "BLI_math.h"
+#include "BLI_stack.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_editmesh.h"
+#include "BKE_global.h"
+#include "BKE_mesh.h"
+
+#include "intern/bmesh_private.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Update Vertex & Face Normals
+ * \{ */
+
+/**
+ * Helpers for #BM_mesh_normals_update and #BM_verts_calc_normal_vcos
+ */
+
+/* We use that existing internal API flag,
+ * assuming no other tool using it would run concurrently to clnors editing. */
+#define BM_LNORSPACE_UPDATE _FLAG_MF
+
+typedef struct BMEdgesCalcVectorsData {
+ /* Read-only data. */
+ const float (*vcos)[3];
+
+ /* Read-write data, but no need to protect it, no concurrency to fear here. */
+ float (*edgevec)[3];
+} BMEdgesCalcVectorsData;
+
+static void bm_edge_calc_vectors_cb(void *userdata,
+ MempoolIterData *mp_e,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMEdge *e = (BMEdge *)mp_e;
+ /* The edge vector will not be needed when the edge has no radial. */
+ if (e->l != NULL) {
+ float(*edgevec)[3] = userdata;
+ float *e_diff = edgevec[BM_elem_index_get(e)];
+ sub_v3_v3v3(e_diff, e->v2->co, e->v1->co);
+ normalize_v3(e_diff);
+ }
+}
+
+static void bm_edge_calc_vectors_with_coords_cb(void *userdata,
+ MempoolIterData *mp_e,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMEdge *e = (BMEdge *)mp_e;
+ /* The edge vector will not be needed when the edge has no radial. */
+ if (e->l != NULL) {
+ BMEdgesCalcVectorsData *data = userdata;
+ float *e_diff = data->edgevec[BM_elem_index_get(e)];
+ sub_v3_v3v3(
+ e_diff, data->vcos[BM_elem_index_get(e->v2)], data->vcos[BM_elem_index_get(e->v1)]);
+ normalize_v3(e_diff);
+ }
+}
+
+static void bm_mesh_edges_calc_vectors(BMesh *bm, float (*edgevec)[3], const float (*vcos)[3])
+{
+ BM_mesh_elem_index_ensure(bm, BM_EDGE | (vcos ? BM_VERT : 0));
+
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+ settings.use_threading = bm->totedge >= BM_OMP_LIMIT;
+
+ if (vcos == NULL) {
+ BM_iter_parallel(bm, BM_EDGES_OF_MESH, bm_edge_calc_vectors_cb, edgevec, &settings);
+ }
+ else {
+ BMEdgesCalcVectorsData data = {
+ .edgevec = edgevec,
+ .vcos = vcos,
+ };
+ BM_iter_parallel(bm, BM_EDGES_OF_MESH, bm_edge_calc_vectors_with_coords_cb, &data, &settings);
+ }
+}
+
+typedef struct BMVertsCalcNormalsWithCoordsData {
+ /* Read-only data. */
+ const float (*fnos)[3];
+ const float (*edgevec)[3];
+ const float (*vcos)[3];
+
+ /* Write data. */
+ float (*vnos)[3];
+} BMVertsCalcNormalsWithCoordsData;
+
+BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter,
+ const float (*edgevec)[3],
+ const float f_no[3],
+ float v_no[3])
+{
+ /* Calculate the dot product of the two edges that meet at the loop's vertex. */
+ const float *e1diff = edgevec[BM_elem_index_get(l_iter->prev->e)];
+ const float *e2diff = edgevec[BM_elem_index_get(l_iter->e)];
+ /* Edge vectors are calculated from e->v1 to e->v2, so adjust the dot product if one but not
+ * both loops actually runs from from e->v2 to e->v1. */
+ float dotprod = dot_v3v3(e1diff, e2diff);
+ if ((l_iter->prev->e->v1 == l_iter->prev->v) ^ (l_iter->e->v1 == l_iter->v)) {
+ dotprod = -dotprod;
+ }
+ const float fac = saacos(-dotprod);
+ /* NAN detection, otherwise this is a degenerated case, ignore that vertex in this case. */
+ if (fac == fac) { /* NAN detection. */
+ madd_v3_v3fl(v_no, f_no, fac);
+ }
+}
+
+static void bm_vert_calc_normals_impl(const float (*edgevec)[3], BMVert *v)
+{
+ float *v_no = v->no;
+ zero_v3(v_no);
+ BMEdge *e_first = v->e;
+ if (e_first != NULL) {
+ BMEdge *e_iter = e_first;
+ do {
+ BMLoop *l_first = e_iter->l;
+ if (l_first != NULL) {
+ BMLoop *l_iter = l_first;
+ do {
+ if (l_iter->v == v) {
+ bm_vert_calc_normals_accum_loop(l_iter, edgevec, l_iter->f->no, v_no);
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+
+ if (LIKELY(normalize_v3(v_no) != 0.0f)) {
+ return;
+ }
+ }
+ /* Fallback normal. */
+ normalize_v3_v3(v_no, v->co);
+}
+
+static void bm_vert_calc_normals_cb(void *userdata,
+ MempoolIterData *mp_v,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ const float(*edgevec)[3] = userdata;
+ BMVert *v = (BMVert *)mp_v;
+ bm_vert_calc_normals_impl(edgevec, v);
+}
+
+static void bm_vert_calc_normals_with_coords(BMVert *v, BMVertsCalcNormalsWithCoordsData *data)
+{
+ float *v_no = data->vnos[BM_elem_index_get(v)];
+ zero_v3(v_no);
+
+ /* Loop over edges. */
+ BMEdge *e_first = v->e;
+ if (e_first != NULL) {
+ BMEdge *e_iter = e_first;
+ do {
+ BMLoop *l_first = e_iter->l;
+ if (l_first != NULL) {
+ BMLoop *l_iter = l_first;
+ do {
+ if (l_iter->v == v) {
+ bm_vert_calc_normals_accum_loop(
+ l_iter, data->edgevec, data->fnos[BM_elem_index_get(l_iter->f)], v_no);
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+
+ if (LIKELY(normalize_v3(v_no) != 0.0f)) {
+ return;
+ }
+ }
+ /* Fallback normal. */
+ normalize_v3_v3(v_no, data->vcos[BM_elem_index_get(v)]);
+}
+
+static void bm_vert_calc_normals_with_coords_cb(void *userdata,
+ MempoolIterData *mp_v,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMVertsCalcNormalsWithCoordsData *data = userdata;
+ BMVert *v = (BMVert *)mp_v;
+ bm_vert_calc_normals_with_coords(v, data);
+}
+
+static void bm_mesh_verts_calc_normals(BMesh *bm,
+ const float (*edgevec)[3],
+ const float (*fnos)[3],
+ const float (*vcos)[3],
+ float (*vnos)[3])
+{
+ BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE) | ((vnos || vcos) ? BM_VERT : 0));
+
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+ settings.use_threading = bm->totvert >= BM_OMP_LIMIT;
+
+ if (vcos == NULL) {
+ BM_iter_parallel(bm, BM_VERTS_OF_MESH, bm_vert_calc_normals_cb, (void *)edgevec, &settings);
+ }
+ else {
+ BLI_assert(!ELEM(NULL, fnos, vnos));
+ BMVertsCalcNormalsWithCoordsData data = {
+ .edgevec = edgevec,
+ .fnos = fnos,
+ .vcos = vcos,
+ .vnos = vnos,
+ };
+ BM_iter_parallel(bm, BM_VERTS_OF_MESH, bm_vert_calc_normals_with_coords_cb, &data, &settings);
+ }
+}
+
+static void bm_face_calc_normals_cb(void *UNUSED(userdata),
+ MempoolIterData *mp_f,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMFace *f = (BMFace *)mp_f;
+
+ BM_face_calc_normal(f, f->no);
+}
+
+/**
+ * \brief BMesh Compute Normals
+ *
+ * Updates the normals of a mesh.
+ */
+void BM_mesh_normals_update(BMesh *bm)
+{
+ float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
+
+ /* Parallel mempool iteration does not allow generating indices inline anymore. */
+ BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE));
+
+ /* Calculate all face normals. */
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+ settings.use_threading = bm->totedge >= BM_OMP_LIMIT;
+
+ BM_iter_parallel(bm, BM_FACES_OF_MESH, bm_face_calc_normals_cb, NULL, &settings);
+
+ bm_mesh_edges_calc_vectors(bm, edgevec, NULL);
+
+ /* Add weighted face normals to vertices, and normalize vert normals. */
+ bm_mesh_verts_calc_normals(bm, edgevec, NULL, NULL, NULL);
+ MEM_freeN(edgevec);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Update Vertex & Face Normals (Partial Updates)
+ * \{ */
+
+static void bm_partial_faces_parallel_range_calc_normals_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMFace *f = ((BMFace **)userdata)[iter];
+ BM_face_calc_normal(f, f->no);
+}
+
+static void bm_partial_edges_parallel_range_calc_vectors_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMEdge *e = ((BMEdge **)((void **)userdata)[0])[iter];
+ float *r_edgevec = ((float(*)[3])((void **)userdata)[1])[iter];
+ sub_v3_v3v3(r_edgevec, e->v1->co, e->v2->co);
+ normalize_v3(r_edgevec);
+}
+
+static void bm_partial_verts_parallel_range_calc_normal_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMVert *v = ((BMVert **)((void **)userdata)[0])[iter];
+ const float(*edgevec)[3] = (const float(*)[3])((void **)userdata)[1];
+ bm_vert_calc_normals_impl(edgevec, v);
+}
+
+/**
+ * A version of #BM_mesh_normals_update that updates a subset of geometry,
+ * used to avoid the overhead of updating everything.
+ */
+void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpinfo)
+{
+ BLI_assert(bmpinfo->params.do_normals);
+
+ BMVert **verts = bmpinfo->verts;
+ BMEdge **edges = bmpinfo->edges;
+ BMFace **faces = bmpinfo->faces;
+ const int verts_len = bmpinfo->verts_len;
+ const int edges_len = bmpinfo->edges_len;
+ const int faces_len = bmpinfo->faces_len;
+
+ float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * edges_len, __func__);
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+
+ /* Faces. */
+ BLI_task_parallel_range(
+ 0, faces_len, faces, bm_partial_faces_parallel_range_calc_normals_cb, &settings);
+
+ /* Temporarily override the edge indices,
+ * storing the correct indices in the case they're not dirty.
+ *
+ * \note in most cases indices are modified and #BMesh.elem_index_dirty is set.
+ * This is an exceptional case where indices are restored because the worst case downside
+ * of marking the edge indices dirty would require a full loop over all edges to
+ * correct the indices in other functions which need them to be valid.
+ * When moving a few vertices on a high poly mesh setting and restoring connected
+ * edges has very little overhead compared with restoring all edge indices. */
+ int *edge_index_value = NULL;
+ if ((bm->elem_index_dirty & BM_EDGE) == 0) {
+ edge_index_value = MEM_mallocN(sizeof(*edge_index_value) * edges_len, __func__);
+
+ for (int i = 0; i < edges_len; i++) {
+ BMEdge *e = edges[i];
+ edge_index_value[i] = BM_elem_index_get(e);
+ BM_elem_index_set(e, i); /* set_dirty! (restore before this function exits). */
+ }
+ }
+ else {
+ for (int i = 0; i < edges_len; i++) {
+ BMEdge *e = edges[i];
+ BM_elem_index_set(e, i); /* set_dirty! (already dirty) */
+ }
+ }
+
+ {
+ /* Verts. */
+
+ /* Compute normalized direction vectors for each edge.
+ * Directions will be used for calculating the weights of the face normals on the vertex
+ * normals. */
+ void *data[2] = {edges, edgevec};
+ BLI_task_parallel_range(
+ 0, edges_len, data, bm_partial_edges_parallel_range_calc_vectors_cb, &settings);
+
+ /* Calculate vertex normals. */
+ data[0] = verts;
+ BLI_task_parallel_range(
+ 0, verts_len, data, bm_partial_verts_parallel_range_calc_normal_cb, &settings);
+ }
+
+ if (edge_index_value != NULL) {
+ for (int i = 0; i < edges_len; i++) {
+ BMEdge *e = edges[i];
+ BM_elem_index_set(e, edge_index_value[i]); /* set_ok (restore) */
+ }
+
+ MEM_freeN(edge_index_value);
+ }
+
+ MEM_freeN(edgevec);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Update Vertex & Face Normals (Custom Coords)
+ * \{ */
+
+/**
+ * \brief BMesh Compute Normals from/to external data.
+ *
+ * Computes the vertex normals of a mesh into vnos,
+ * using given vertex coordinates (vcos) and polygon normals (fnos).
+ */
+void BM_verts_calc_normal_vcos(BMesh *bm,
+ const float (*fnos)[3],
+ const float (*vcos)[3],
+ float (*vnos)[3])
+{
+ float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
+
+ /* Compute normalized direction vectors for each edge.
+ * Directions will be used for calculating the weights of the face normals on the vertex normals.
+ */
+ bm_mesh_edges_calc_vectors(bm, edgevec, vcos);
+
+ /* Add weighted face normals to vertices, and normalize vert normals. */
+ bm_mesh_verts_calc_normals(bm, edgevec, fnos, vcos, vnos);
+ MEM_freeN(edgevec);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Tagging Utility Functions
+ * \{ */
+
+void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
+{
+ BMFace *f;
+ BMEdge *e;
+ BMIter fiter, eiter;
+ BMLoop *l_curr, *l_first;
+
+ if (do_edges) {
+ int index_edge;
+ BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, index_edge) {
+ BMLoop *l_a, *l_b;
+
+ BM_elem_index_set(e, index_edge); /* set_inline */
+ BM_elem_flag_disable(e, BM_ELEM_TAG);
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+ if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+ }
+ }
+ }
+ bm->elem_index_dirty &= ~BM_EDGE;
+ }
+
+ int index_face, index_loop = 0;
+ BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, index_face) {
+ BM_elem_index_set(f, index_face); /* set_inline */
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_elem_index_set(l_curr, index_loop++); /* set_inline */
+ BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+}
+
+/**
+ * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normal_vcos
+ */
+static void bm_mesh_edges_sharp_tag(BMesh *bm,
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ const float split_angle,
+ const bool do_sharp_edges_tag)
+{
+ BMIter eiter;
+ BMEdge *e;
+ int i;
+
+ const bool check_angle = (split_angle < (float)M_PI);
+ const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f;
+
+ {
+ char htype = BM_VERT | BM_LOOP;
+ if (fnos) {
+ htype |= BM_FACE;
+ }
+ BM_mesh_elem_index_ensure(bm, htype);
+ }
+
+ /* This first loop checks which edges are actually smooth,
+ * and pre-populate lnos with vnos (as if they were all smooth). */
+ BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
+ BMLoop *l_a, *l_b;
+
+ BM_elem_index_set(e, i); /* set_inline */
+ BM_elem_flag_disable(e, BM_ELEM_TAG); /* Clear tag (means edge is sharp). */
+
+ /* An edge with only two loops, might be smooth... */
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+ bool is_angle_smooth = true;
+ if (check_angle) {
+ const float *no_a = fnos ? fnos[BM_elem_index_get(l_a->f)] : l_a->f->no;
+ const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no;
+ is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle_cos);
+ }
+
+ /* We only tag edges that are *really* smooth:
+ * If the angle between both its polys' normals is below split_angle value,
+ * and it is tagged as such,
+ * and both its faces are smooth,
+ * and both its faces have compatible (non-flipped) normals,
+ * i.e. both loops on the same edge do not share the same vertex.
+ */
+ if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) &&
+ BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
+ if (is_angle_smooth) {
+ const float *no;
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+
+ /* linked vertices might be fully smooth, copy their normals to loop ones. */
+ if (r_lnos) {
+ no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no;
+ copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no);
+ no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no;
+ copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no);
+ }
+ }
+ else if (do_sharp_edges_tag) {
+ /* Note that we do not care about the other sharp-edge cases
+ * (sharp poly, non-manifold edge, etc.),
+ * only tag edge as sharp when it is due to angle threshold. */
+ BM_elem_flag_disable(e, BM_ELEM_SMOOTH);
+ }
+ }
+ }
+ }
+
+ bm->elem_index_dirty &= ~BM_EDGE;
+}
+
+/**
+ * Define sharp edges as needed to mimic 'autosmooth' from angle threshold.
+ *
+ * Used when defining an empty custom loop normals data layer,
+ * to keep same shading as with auto-smooth!
+ */
+void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
+{
+ if (split_angle >= (float)M_PI) {
+ /* Nothing to do! */
+ return;
+ }
+
+ bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop Normals Calculation API
+ * \{ */
+
+/**
+ * Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not.
+ * Needed because cyclic smooth fans have no obvious 'entry point',
+ * and yet we need to walk them once, and only once.
+ */
+bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
+{
+ BMLoop *lfan_pivot_next = l_curr;
+ BMEdge *e_next = l_curr->e;
+
+ BLI_assert(!BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG));
+ BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
+
+ while (true) {
+ /* Much simpler than in sibling code with basic Mesh data! */
+ lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot_next, &e_next);
+
+ if (!lfan_pivot_next || !BM_elem_flag_test(e_next, BM_ELEM_TAG)) {
+ /* Sharp loop/edge, so not a cyclic smooth fan... */
+ return false;
+ }
+ /* Smooth loop/edge... */
+ if (BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG)) {
+ if (lfan_pivot_next == l_curr) {
+ /* We walked around a whole cyclic smooth fan
+ * without finding any already-processed loop,
+ * means we can use initial l_curr/l_prev edge as start for this smooth fan. */
+ return true;
+ }
+ /* ... already checked in some previous looping, we can abort. */
+ return false;
+ }
+ /* ... we can skip it in future, and keep checking the smooth fan. */
+ BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
+ }
+}
+
+/**
+ * BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c
+ * Will use first clnors_data array, and fallback to cd_loop_clnors_offset
+ * (use NULL and -1 to not use clnors).
+ *
+ * \note This sets #BM_ELEM_TAG which is used in tool code (e.g. T84426).
+ * we could add a low-level API flag for this, see #BM_ELEM_API_FLAG_ENABLE and friends.
+ */
+static void bm_mesh_loops_calc_normals(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild)
+{
+ BMIter fiter;
+ BMFace *f_curr;
+ const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
+
+ MLoopNorSpaceArray _lnors_spacearr = {NULL};
+
+ /* Temp normal stack. */
+ BLI_SMALLSTACK_DECLARE(normal, float *);
+ /* Temp clnors stack. */
+ BLI_SMALLSTACK_DECLARE(clnors, short *);
+ /* Temp edge vectors stack, only used when computing lnor spacearr. */
+ BLI_Stack *edge_vectors = NULL;
+
+ {
+ char htype = 0;
+ if (vcos) {
+ htype |= BM_VERT;
+ }
+ /* Face/Loop indices are set inline below. */
+ BM_mesh_elem_index_ensure(bm, htype);
+ }
+
+ if (!r_lnors_spacearr && has_clnors) {
+ /* We need to compute lnor spacearr if some custom lnor data are given to us! */
+ r_lnors_spacearr = &_lnors_spacearr;
+ }
+ if (r_lnors_spacearr) {
+ BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
+ edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
+ }
+
+ /* Clear all loops' tags (means none are to be skipped for now). */
+ int index_face, index_loop = 0;
+ BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) {
+ BMLoop *l_curr, *l_first;
+
+ BM_elem_index_set(f_curr, index_face); /* set_inline */
+
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
+ do {
+ BM_elem_index_set(l_curr, index_loop++); /* set_inline */
+ BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+
+ /* We now know edges that can be smoothed (they are tagged),
+ * and edges that will be hard (they aren't).
+ * Now, time to generate the normals.
+ */
+ BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_curr, *l_first;
+
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
+ do {
+ if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
+ !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) {
+ continue;
+ }
+ /* A smooth edge, we have to check for cyclic smooth fan case.
+ * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge
+ * as 'entry point', otherwise we can skip it. */
+
+ /* Note: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store
+ * mlfan_pivot's in a stack, to avoid having to fan again around
+ * the vert during actual computation of clnor & clnorspace. However, this would complicate
+ * the code, add more memory usage, and
+ * BM_vert_step_fan_loop() is quite cheap in term of CPU cycles,
+ * so really think it's not worth it. */
+ if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
+ (BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !BM_loop_check_cyclic_smooth_fan(l_curr))) {
+ }
+ else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
+ !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) {
+ /* Simple case (both edges around that vertex are sharp in related polygon),
+ * this vertex just takes its poly normal.
+ */
+ const int l_curr_index = BM_elem_index_get(l_curr);
+ const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no;
+ copy_v3_v3(r_lnos[l_curr_index], no);
+
+ /* If needed, generate this (simple!) lnor space. */
+ if (r_lnors_spacearr) {
+ float vec_curr[3], vec_prev[3];
+ MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr);
+
+ {
+ const BMVert *v_pivot = l_curr->v;
+ const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
+ const BMVert *v_1 = BM_edge_other_vert(l_curr->e, v_pivot);
+ const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co;
+ const BMVert *v_2 = BM_edge_other_vert(l_curr->prev->e, v_pivot);
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ sub_v3_v3v3(vec_curr, co_1, co_pivot);
+ normalize_v3(vec_curr);
+ sub_v3_v3v3(vec_prev, co_2, co_pivot);
+ normalize_v3(vec_prev);
+ }
+
+ BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL);
+ /* We know there is only one loop in this space,
+ * no need to create a linklist in this case... */
+ BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true);
+
+ if (has_clnors) {
+ const short(*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] :
+ (const void *)BM_ELEM_CD_GET_VOID_P(
+ l_curr, cd_loop_clnors_offset);
+ BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]);
+ }
+ }
+ }
+ /* We *do not need* to check/tag loops as already computed!
+ * Due to the fact a loop only links to one of its two edges,
+ * a same fan *will never be walked more than once!*
+ * Since we consider edges having neighbor faces with inverted (flipped) normals as sharp,
+ * we are sure that no fan will be skipped, even only considering the case
+ * (sharp curr_edge, smooth prev_edge), and not the alternative
+ * (smooth curr_edge, sharp prev_edge).
+ * All this due/thanks to link between normals and loop ordering.
+ */
+ else {
+ /* We have to fan around current vertex, until we find the other non-smooth edge,
+ * and accumulate face normals into the vertex!
+ * Note in case this vertex has only one sharp edge,
+ * this is a waste because the normal is the same as the vertex normal,
+ * but I do not see any easy way to detect that (would need to count number of sharp edges
+ * per vertex, I doubt the additional memory usage would be worth it, especially as it
+ * should not be a common case in real-life meshes anyway).
+ */
+ BMVert *v_pivot = l_curr->v;
+ BMEdge *e_next;
+ const BMEdge *e_org = l_curr->e;
+ BMLoop *lfan_pivot, *lfan_pivot_next;
+ int lfan_pivot_index;
+ float lnor[3] = {0.0f, 0.0f, 0.0f};
+ float vec_curr[3], vec_next[3], vec_org[3];
+
+ /* We validate clnors data on the fly - cheapest way to do! */
+ int clnors_avg[2] = {0, 0};
+ const short(*clnor_ref)[2] = NULL;
+ int clnors_nbr = 0;
+ bool clnors_invalid = false;
+
+ const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
+
+ MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) :
+ NULL;
+
+ BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors));
+
+ lfan_pivot = l_curr;
+ lfan_pivot_index = BM_elem_index_get(lfan_pivot);
+ e_next = lfan_pivot->e; /* Current edge here, actually! */
+
+ /* Only need to compute previous edge's vector once,
+ * then we can just reuse old current one! */
+ {
+ const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ sub_v3_v3v3(vec_org, co_2, co_pivot);
+ normalize_v3(vec_org);
+ copy_v3_v3(vec_curr, vec_org);
+
+ if (r_lnors_spacearr) {
+ BLI_stack_push(edge_vectors, vec_org);
+ }
+ }
+
+ while (true) {
+ /* Much simpler than in sibling code with basic Mesh data! */
+ lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
+ if (lfan_pivot_next) {
+ BLI_assert(lfan_pivot_next->v == v_pivot);
+ }
+ else {
+ /* next edge is non-manifold, we have to find it ourselves! */
+ e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
+ }
+
+ /* Compute edge vector.
+ * NOTE: We could pre-compute those into an array, in the first iteration,
+ * instead of computing them twice (or more) here.
+ * However, time gained is not worth memory and time lost,
+ * given the fact that this code should not be called that much in real-life meshes.
+ */
+ {
+ const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ sub_v3_v3v3(vec_next, co_2, co_pivot);
+ normalize_v3(vec_next);
+ }
+
+ {
+ /* Code similar to accumulate_vertex_normals_poly_v3. */
+ /* Calculate angle between the two poly edges incident on this vertex. */
+ const BMFace *f = lfan_pivot->f;
+ const float fac = saacos(dot_v3v3(vec_next, vec_curr));
+ const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
+ /* Accumulate */
+ madd_v3_v3fl(lnor, no, fac);
+
+ if (has_clnors) {
+ /* Accumulate all clnors, if they are not all equal we have to fix that! */
+ const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] :
+ (const void *)BM_ELEM_CD_GET_VOID_P(
+ lfan_pivot, cd_loop_clnors_offset);
+ if (clnors_nbr) {
+ clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] ||
+ (*clnor_ref)[1] != (*clnor)[1]);
+ }
+ else {
+ clnor_ref = clnor;
+ }
+ clnors_avg[0] += (*clnor)[0];
+ clnors_avg[1] += (*clnor)[1];
+ clnors_nbr++;
+ /* We store here a pointer to all custom lnors processed. */
+ BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
+ }
+ }
+
+ /* We store here a pointer to all loop-normals processed. */
+ BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]);
+
+ if (r_lnors_spacearr) {
+ /* Assign current lnor space to current 'vertex' loop. */
+ BKE_lnor_space_add_loop(
+ r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false);
+ if (e_next != e_org) {
+ /* We store here all edges-normalized vectors processed. */
+ BLI_stack_push(edge_vectors, vec_next);
+ }
+ }
+
+ if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
+ /* Next edge is sharp, we have finished with this fan of faces around this vert! */
+ break;
+ }
+
+ /* Copy next edge vector to current one. */
+ copy_v3_v3(vec_curr, vec_next);
+ /* Next pivot loop to current one. */
+ lfan_pivot = lfan_pivot_next;
+ lfan_pivot_index = BM_elem_index_get(lfan_pivot);
+ }
+
+ {
+ float lnor_len = normalize_v3(lnor);
+
+ /* If we are generating lnor spacearr, we can now define the one for this fan. */
+ if (r_lnors_spacearr) {
+ if (UNLIKELY(lnor_len == 0.0f)) {
+ /* Use vertex normal as fallback! */
+ copy_v3_v3(lnor, r_lnos[lfan_pivot_index]);
+ lnor_len = 1.0f;
+ }
+
+ BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors);
+
+ if (has_clnors) {
+ if (clnors_invalid) {
+ short *clnor;
+
+ clnors_avg[0] /= clnors_nbr;
+ clnors_avg[1] /= clnors_nbr;
+ /* Fix/update all clnors of this fan with computed average value. */
+
+ /* Prints continuously when merge custom normals, so commenting. */
+ /* printf("Invalid clnors in this fan!\n"); */
+
+ while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
+ // print_v2("org clnor", clnor);
+ clnor[0] = (short)clnors_avg[0];
+ clnor[1] = (short)clnors_avg[1];
+ }
+ // print_v2("new clnors", clnors_avg);
+ }
+ else {
+ /* We still have to consume the stack! */
+ while (BLI_SMALLSTACK_POP(clnors)) {
+ /* pass */
+ }
+ }
+ BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor);
+ }
+ }
+
+ /* In case we get a zero normal here, just use vertex normal already set! */
+ if (LIKELY(lnor_len != 0.0f)) {
+ /* Copy back the final computed normal into all related loop-normals. */
+ float *nor;
+
+ while ((nor = BLI_SMALLSTACK_POP(normal))) {
+ copy_v3_v3(nor, lnor);
+ }
+ }
+ else {
+ /* We still have to consume the stack! */
+ while (BLI_SMALLSTACK_POP(normal)) {
+ /* pass */
+ }
+ }
+ }
+
+ /* Tag related vertex as sharp, to avoid fanning around it again
+ * (in case it was a smooth one). */
+ if (r_lnors_spacearr) {
+ BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG);
+ }
+ }
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+
+ if (r_lnors_spacearr) {
+ BLI_stack_free(edge_vectors);
+ if (r_lnors_spacearr == &_lnors_spacearr) {
+ BKE_lnor_spacearr_free(r_lnors_spacearr);
+ }
+ }
+}
+
+/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
+#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
+
+/**
+ * Check each current smooth fan (one lnor space per smooth fan!), and if all its
+ * matching custom lnors are not (enough) equal, add sharp edges as needed.
+ */
+static bool bm_mesh_loops_split_lnor_fans(BMesh *bm,
+ MLoopNorSpaceArray *lnors_spacearr,
+ const float (*new_lnors)[3])
+{
+ BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
+ bool changed = false;
+
+ BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
+
+ for (int i = 0; i < bm->totloop; i++) {
+ if (!lnors_spacearr->lspacearr[i]) {
+ /* This should not happen in theory, but in some rare case (probably ugly geometry)
+ * we can get some NULL loopspacearr at this point. :/
+ * Maybe we should set those loops' edges as sharp?
+ */
+ BLI_BITMAP_ENABLE(done_loops, i);
+ if (G.debug & G_DEBUG) {
+ printf("WARNING! Getting invalid NULL loop space for loop %d!\n", i);
+ }
+ continue;
+ }
+
+ if (!BLI_BITMAP_TEST(done_loops, i)) {
+ /* Notes:
+ * * In case of mono-loop smooth fan, we have nothing to do.
+ * * Loops in this linklist are ordered (in reversed order compared to how they were
+ * discovered by BKE_mesh_normals_loop_split(), but this is not a problem).
+ * Which means if we find a mismatching clnor,
+ * we know all remaining loops will have to be in a new, different smooth fan/lnor space.
+ * * In smooth fan case, we compare each clnor against a ref one,
+ * to avoid small differences adding up into a real big one in the end!
+ */
+ if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
+ BLI_BITMAP_ENABLE(done_loops, i);
+ continue;
+ }
+
+ LinkNode *loops = lnors_spacearr->lspacearr[i]->loops;
+ BMLoop *prev_ml = NULL;
+ const float *org_nor = NULL;
+
+ while (loops) {
+ BMLoop *ml = loops->link;
+ const int lidx = BM_elem_index_get(ml);
+ const float *nor = new_lnors[lidx];
+
+ if (!org_nor) {
+ org_nor = nor;
+ }
+ else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
+ /* Current normal differs too much from org one, we have to tag the edge between
+ * previous loop's face and current's one as sharp.
+ * We know those two loops do not point to the same edge,
+ * since we do not allow reversed winding in a same smooth fan.
+ */
+ BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e;
+
+ BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH);
+ changed = true;
+
+ org_nor = nor;
+ }
+
+ prev_ml = ml;
+ loops = loops->next;
+ BLI_BITMAP_ENABLE(done_loops, lidx);
+ }
+
+ /* We also have to check between last and first loops,
+ * otherwise we may miss some sharp edges here!
+ * This is just a simplified version of above while loop.
+ * See T45984. */
+ loops = lnors_spacearr->lspacearr[i]->loops;
+ if (loops && org_nor) {
+ BMLoop *ml = loops->link;
+ const int lidx = BM_elem_index_get(ml);
+ const float *nor = new_lnors[lidx];
+
+ if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
+ BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e;
+
+ BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH);
+ changed = true;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(done_loops);
+ return changed;
+}
+
+/**
+ * Assign custom normal data from given normal vectors, averaging normals
+ * from one smooth fan as necessary.
+ */
+static void bm_mesh_loops_assign_normal_data(BMesh *bm,
+ MLoopNorSpaceArray *lnors_spacearr,
+ short (*r_clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const float (*new_lnors)[3])
+{
+ BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
+
+ BLI_SMALLSTACK_DECLARE(clnors_data, short *);
+
+ BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
+
+ for (int i = 0; i < bm->totloop; i++) {
+ if (!lnors_spacearr->lspacearr[i]) {
+ BLI_BITMAP_ENABLE(done_loops, i);
+ if (G.debug & G_DEBUG) {
+ printf("WARNING! Still getting invalid NULL loop space in second loop for loop %d!\n", i);
+ }
+ continue;
+ }
+
+ if (!BLI_BITMAP_TEST(done_loops, i)) {
+ /* Note we accumulate and average all custom normals in current smooth fan,
+ * to avoid getting different clnors data (tiny differences in plain custom normals can
+ * give rather huge differences in computed 2D factors).
+ */
+ LinkNode *loops = lnors_spacearr->lspacearr[i]->loops;
+
+ if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
+ BMLoop *ml = (BMLoop *)loops;
+ const int lidx = BM_elem_index_get(ml);
+
+ BLI_assert(lidx == i);
+
+ const float *nor = new_lnors[lidx];
+ short *clnor = r_clnors_data ? &r_clnors_data[lidx] :
+ BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset);
+
+ BKE_lnor_space_custom_normal_to_data(lnors_spacearr->lspacearr[i], nor, clnor);
+ BLI_BITMAP_ENABLE(done_loops, i);
+ }
+ else {
+ int nbr_nors = 0;
+ float avg_nor[3];
+ short clnor_data_tmp[2], *clnor_data;
+
+ zero_v3(avg_nor);
+
+ while (loops) {
+ BMLoop *ml = loops->link;
+ const int lidx = BM_elem_index_get(ml);
+ const float *nor = new_lnors[lidx];
+ short *clnor = r_clnors_data ? &r_clnors_data[lidx] :
+ BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset);
+
+ nbr_nors++;
+ add_v3_v3(avg_nor, nor);
+ BLI_SMALLSTACK_PUSH(clnors_data, clnor);
+
+ loops = loops->next;
+ BLI_BITMAP_ENABLE(done_loops, lidx);
+ }
+
+ mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors);
+ BKE_lnor_space_custom_normal_to_data(
+ lnors_spacearr->lspacearr[i], avg_nor, clnor_data_tmp);
+
+ while ((clnor_data = BLI_SMALLSTACK_POP(clnors_data))) {
+ clnor_data[0] = clnor_data_tmp[0];
+ clnor_data[1] = clnor_data_tmp[1];
+ }
+ }
+ }
+ }
+
+ MEM_freeN(done_loops);
+}
+
+/**
+ * Compute internal representation of given custom normals (as an array of float[2] or data layer).
+ *
+ * It also makes sure the mesh matches those custom normals, by marking new sharp edges to split
+ * the smooth fans when loop normals for the same vertex are different, or averaging the normals
+ * instead, depending on the do_split_fans parameter.
+ */
+static void bm_mesh_loops_custom_normals_set(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ short (*r_clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ float (*new_lnors)[3],
+ const int cd_new_lnors_offset,
+ bool do_split_fans)
+{
+ BMFace *f;
+ BMLoop *l;
+ BMIter liter, fiter;
+ float(*cur_lnors)[3] = MEM_mallocN(sizeof(*cur_lnors) * bm->totloop, __func__);
+
+ BKE_lnor_spacearr_clear(r_lnors_spacearr);
+
+ /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
+ * When using custom loop normals, disable the angle feature! */
+ bm_mesh_edges_sharp_tag(bm, vnos, fnos, cur_lnors, (float)M_PI, false);
+
+ /* Finish computing lnos by accumulating face normals
+ * in each fan of faces defined by sharp edges. */
+ bm_mesh_loops_calc_normals(
+ bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
+
+ /* Extract new normals from the data layer if necessary. */
+ float(*custom_lnors)[3] = new_lnors;
+
+ if (new_lnors == NULL) {
+ custom_lnors = MEM_mallocN(sizeof(*new_lnors) * bm->totloop, __func__);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ const float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_new_lnors_offset);
+ copy_v3_v3(custom_lnors[BM_elem_index_get(l)], normal);
+ }
+ }
+ }
+
+ /* Validate the new normals. */
+ for (int i = 0; i < bm->totloop; i++) {
+ if (is_zero_v3(custom_lnors[i])) {
+ copy_v3_v3(custom_lnors[i], cur_lnors[i]);
+ }
+ else {
+ normalize_v3(custom_lnors[i]);
+ }
+ }
+
+ /* Now, check each current smooth fan (one lnor space per smooth fan!),
+ * and if all its matching custom lnors are not equal, add sharp edges as needed. */
+ if (do_split_fans && bm_mesh_loops_split_lnor_fans(bm, r_lnors_spacearr, custom_lnors)) {
+ /* If any sharp edges were added, run bm_mesh_loops_calc_normals() again to get lnor
+ * spacearr/smooth fans matching the given custom lnors. */
+ BKE_lnor_spacearr_clear(r_lnors_spacearr);
+
+ bm_mesh_loops_calc_normals(
+ bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
+ }
+
+ /* And we just have to convert plain object-space custom normals to our
+ * lnor space-encoded ones. */
+ bm_mesh_loops_assign_normal_data(
+ bm, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, custom_lnors);
+
+ MEM_freeN(cur_lnors);
+
+ if (custom_lnors != new_lnors) {
+ MEM_freeN(custom_lnors);
+ }
+}
+
+static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm,
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3])
+{
+ BMIter fiter;
+ BMFace *f_curr;
+
+ {
+ char htype = BM_LOOP;
+ if (vnos) {
+ htype |= BM_VERT;
+ }
+ if (fnos) {
+ htype |= BM_FACE;
+ }
+ BM_mesh_elem_index_ensure(bm, htype);
+ }
+
+ BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_curr, *l_first;
+ const bool is_face_flat = !BM_elem_flag_test(f_curr, BM_ELEM_SMOOTH);
+
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
+ do {
+ const float *no = is_face_flat ? (fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no) :
+ (vnos ? vnos[BM_elem_index_get(l_curr->v)] : l_curr->v->no);
+ copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no);
+
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+}
+
+#if 0 /* Unused currently */
+/**
+ * \brief BMesh Compute Loop Normals
+ *
+ * Updates the loop normals of a mesh.
+ * Assumes vertex and face normals are valid (else call BM_mesh_normals_update() first)!
+ */
+void BM_mesh_loop_normals_update(BMesh *bm,
+ const bool use_split_normals,
+ const float split_angle,
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset)
+{
+ const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
+
+ if (use_split_normals) {
+ /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
+ * When using custom loop normals, disable the angle feature! */
+ bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos);
+
+ /* Finish computing lnos by accumulating face normals
+ * in each fan of faces defined by sharp edges. */
+ bm_mesh_loops_calc_normals(
+ bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
+ }
+ else {
+ BLI_assert(!r_lnors_spacearr);
+ bm_mesh_loops_calc_normals_no_autosmooth(bm, NULL, NULL, r_lnos);
+ }
+}
+#endif
+
+/**
+ * \brief BMesh Compute Loop Normals from/to external data.
+ *
+ * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
+ * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry
+ * (splitting edges).
+ */
+void BM_loops_calc_normal_vcos(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ const bool use_split_normals,
+ const float split_angle,
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild)
+{
+ const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
+
+ if (use_split_normals) {
+ /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
+ * When using custom loop normals, disable the angle feature! */
+ bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false);
+
+ /* Finish computing lnos by accumulating face normals
+ * in each fan of faces defined by sharp edges. */
+ bm_mesh_loops_calc_normals(
+ bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild);
+ }
+ else {
+ BLI_assert(!r_lnors_spacearr);
+ bm_mesh_loops_calc_normals_no_autosmooth(bm, vnos, fnos, r_lnos);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop Normal Space API
+ * \{ */
+
+void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3])
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
+ }
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_loops_calc_normal_vcos(bm,
+ NULL,
+ NULL,
+ NULL,
+ true,
+ M_PI,
+ r_lnors,
+ bm->lnor_spacearr,
+ NULL,
+ cd_loop_clnors_offset,
+ false);
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+}
+
+#define CLEAR_SPACEARRAY_THRESHOLD(x) ((x) / 2)
+
+void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
+{
+ if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ return;
+ }
+ if (do_invalidate_all || bm->totvertsel > CLEAR_SPACEARRAY_THRESHOLD(bm->totvert)) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ return;
+ }
+ if (bm->lnor_spacearr == NULL) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ return;
+ }
+
+ BMVert *v;
+ BMLoop *l;
+ BMIter viter, liter;
+ /* Note: we could use temp tag of BMItem for that,
+ * but probably better not use it in such a low-level func?
+ * --mont29 */
+ BLI_bitmap *done_verts = BLI_BITMAP_NEW(bm->totvert, __func__);
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ /* When we affect a given vertex, we may affect following smooth fans:
+ * - all smooth fans of said vertex;
+ * - all smooth fans of all immediate loop-neighbors vertices;
+ * This can be simplified as 'all loops of selected vertices and their immediate neighbors'
+ * need to be tagged for update.
+ */
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l, BM_LNORSPACE_UPDATE);
+
+ /* Note that we only handle unselected neighbor vertices here, main loop will take care of
+ * selected ones. */
+ if ((!BM_elem_flag_test(l->prev->v, BM_ELEM_SELECT)) &&
+ !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->prev->v))) {
+
+ BMLoop *l_prev;
+ BMIter liter_prev;
+ BM_ITER_ELEM (l_prev, &liter_prev, l->prev->v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l_prev, BM_LNORSPACE_UPDATE);
+ }
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_prev->v));
+ }
+
+ if ((!BM_elem_flag_test(l->next->v, BM_ELEM_SELECT)) &&
+ !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->next->v))) {
+
+ BMLoop *l_next;
+ BMIter liter_next;
+ BM_ITER_ELEM (l_next, &liter_next, l->next->v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l_next, BM_LNORSPACE_UPDATE);
+ }
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_next->v));
+ }
+ }
+
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(v));
+ }
+ }
+
+ MEM_freeN(done_verts);
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY;
+}
+
+void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+
+ if (!(bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL))) {
+ return;
+ }
+ BMFace *f;
+ BMLoop *l;
+ BMIter fiter, liter;
+
+ float(*r_lnors)[3] = MEM_callocN(sizeof(*r_lnors) * bm->totloop, __func__);
+ float(*oldnors)[3] = preserve_clnor ? MEM_mallocN(sizeof(*oldnors) * bm->totloop, __func__) :
+ NULL;
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ if (preserve_clnor) {
+ BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) ||
+ bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ int l_index = BM_elem_index_get(l);
+
+ BKE_lnor_space_custom_data_to_normal(
+ bm->lnor_spacearr->lspacearr[l_index], *clnor, oldnors[l_index]);
+ }
+ }
+ }
+ }
+
+ if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ BKE_lnor_spacearr_clear(bm->lnor_spacearr);
+ }
+ BM_loops_calc_normal_vcos(bm,
+ NULL,
+ NULL,
+ NULL,
+ true,
+ M_PI,
+ r_lnors,
+ bm->lnor_spacearr,
+ NULL,
+ cd_loop_clnors_offset,
+ true);
+ MEM_freeN(r_lnors);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) ||
+ bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ if (preserve_clnor) {
+ short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ int l_index = BM_elem_index_get(l);
+ BKE_lnor_space_custom_normal_to_data(
+ bm->lnor_spacearr->lspacearr[l_index], oldnors[l_index], *clnor);
+ }
+ BM_ELEM_API_FLAG_DISABLE(l, BM_LNORSPACE_UPDATE);
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(oldnors);
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+
+#ifndef NDEBUG
+ BM_lnorspace_err(bm);
+#endif
+}
+
+/**
+ * \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
+ * take care to run this before setting up tags.
+ */
+void BM_lnorspace_update(BMesh *bm)
+{
+ if (bm->lnor_spacearr == NULL) {
+ bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
+ }
+ if (bm->lnor_spacearr->lspacearr == NULL) {
+ float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
+
+ BM_lnorspacearr_store(bm, lnors);
+
+ MEM_freeN(lnors);
+ }
+ else if (bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)) {
+ BM_lnorspace_rebuild(bm, false);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop Normal Edit Data Array API
+ *
+ * Utilities for creating/freeing #BMLoopNorEditDataArray.
+ * \{ */
+
+/**
+ * Auxiliary function only used by rebuild to detect if any spaces were not marked as invalid.
+ * Reports error if any of the lnor spaces change after rebuilding, meaning that all the possible
+ * lnor spaces to be rebuilt were not correctly marked.
+ */
+#ifndef NDEBUG
+void BM_lnorspace_err(BMesh *bm)
+{
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ bool clear = true;
+
+ MLoopNorSpaceArray *temp = MEM_callocN(sizeof(*temp), __func__);
+ temp->lspacearr = NULL;
+
+ BKE_lnor_spacearr_init(temp, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+ float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
+ BM_loops_calc_normal_vcos(
+ bm, NULL, NULL, NULL, true, M_PI, lnors, temp, NULL, cd_loop_clnors_offset, true);
+
+ for (int i = 0; i < bm->totloop; i++) {
+ int j = 0;
+ j += compare_ff(
+ temp->lspacearr[i]->ref_alpha, bm->lnor_spacearr->lspacearr[i]->ref_alpha, 1e-4f);
+ j += compare_ff(
+ temp->lspacearr[i]->ref_beta, bm->lnor_spacearr->lspacearr[i]->ref_beta, 1e-4f);
+ j += compare_v3v3(
+ temp->lspacearr[i]->vec_lnor, bm->lnor_spacearr->lspacearr[i]->vec_lnor, 1e-4f);
+ j += compare_v3v3(
+ temp->lspacearr[i]->vec_ortho, bm->lnor_spacearr->lspacearr[i]->vec_ortho, 1e-4f);
+ j += compare_v3v3(
+ temp->lspacearr[i]->vec_ref, bm->lnor_spacearr->lspacearr[i]->vec_ref, 1e-4f);
+
+ if (j != 5) {
+ clear = false;
+ break;
+ }
+ }
+ BKE_lnor_spacearr_free(temp);
+ MEM_freeN(temp);
+ MEM_freeN(lnors);
+ BLI_assert(clear);
+
+ bm->spacearr_dirty &= ~BM_SPACEARR_DIRTY_ALL;
+}
+#endif
+
+static void bm_loop_normal_mark_indiv_do_loop(BMLoop *l,
+ BLI_bitmap *loops,
+ MLoopNorSpaceArray *lnor_spacearr,
+ int *totloopsel,
+ const bool do_all_loops_of_vert)
+{
+ if (l != NULL) {
+ const int l_idx = BM_elem_index_get(l);
+
+ if (!BLI_BITMAP_TEST(loops, l_idx)) {
+ /* If vert and face selected share a loop, mark it for editing. */
+ BLI_BITMAP_ENABLE(loops, l_idx);
+ (*totloopsel)++;
+
+ if (do_all_loops_of_vert) {
+ /* If required, also mark all loops shared by that vertex.
+ * This is needed when loop spaces may change
+ * (i.e. when some faces or edges might change of smooth/sharp status). */
+ BMIter liter;
+ BMLoop *lfan;
+ BM_ITER_ELEM (lfan, &liter, l->v, BM_LOOPS_OF_VERT) {
+ const int lfan_idx = BM_elem_index_get(lfan);
+ if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
+ BLI_BITMAP_ENABLE(loops, lfan_idx);
+ (*totloopsel)++;
+ }
+ }
+ }
+ else {
+ /* Mark all loops in same loop normal space (aka smooth fan). */
+ if ((lnor_spacearr->lspacearr[l_idx]->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
+ for (LinkNode *node = lnor_spacearr->lspacearr[l_idx]->loops; node; node = node->next) {
+ const int lfan_idx = BM_elem_index_get((BMLoop *)node->link);
+ if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
+ BLI_BITMAP_ENABLE(loops, lfan_idx);
+ (*totloopsel)++;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Mark the individual clnors to be edited, if multiple selection methods are used. */
+static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops, const bool do_all_loops_of_vert)
+{
+ BMEditSelection *ese, *ese_prev;
+ int totloopsel = 0;
+
+ const bool sel_verts = (bm->selectmode & SCE_SELECT_VERTEX) != 0;
+ const bool sel_edges = (bm->selectmode & SCE_SELECT_EDGE) != 0;
+ const bool sel_faces = (bm->selectmode & SCE_SELECT_FACE) != 0;
+ const bool use_sel_face_history = sel_faces && (sel_edges || sel_verts);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BLI_assert(bm->lnor_spacearr != NULL);
+ BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
+
+ if (use_sel_face_history) {
+ /* Using face history allows to select a single loop from a single face...
+ * Note that this is O(n^2) piece of code,
+ * but it is not designed to be used with huge selection sets,
+ * rather with only a few items selected at most.*/
+ /* Goes from last selected to the first selected element. */
+ for (ese = bm->selected.last; ese; ese = ese->prev) {
+ if (ese->htype == BM_FACE) {
+ /* If current face is selected,
+ * then any verts to be edited must have been selected before it. */
+ for (ese_prev = ese->prev; ese_prev; ese_prev = ese_prev->prev) {
+ if (ese_prev->htype == BM_VERT) {
+ bm_loop_normal_mark_indiv_do_loop(
+ BM_face_vert_share_loop((BMFace *)ese->ele, (BMVert *)ese_prev->ele),
+ loops,
+ bm->lnor_spacearr,
+ &totloopsel,
+ do_all_loops_of_vert);
+ }
+ else if (ese_prev->htype == BM_EDGE) {
+ BMEdge *e = (BMEdge *)ese_prev->ele;
+ bm_loop_normal_mark_indiv_do_loop(BM_face_vert_share_loop((BMFace *)ese->ele, e->v1),
+ loops,
+ bm->lnor_spacearr,
+ &totloopsel,
+ do_all_loops_of_vert);
+
+ bm_loop_normal_mark_indiv_do_loop(BM_face_vert_share_loop((BMFace *)ese->ele, e->v2),
+ loops,
+ bm->lnor_spacearr,
+ &totloopsel,
+ do_all_loops_of_vert);
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (sel_faces) {
+ /* Only select all loops of selected faces. */
+ BMLoop *l;
+ BMFace *f;
+ BMIter liter, fiter;
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ bm_loop_normal_mark_indiv_do_loop(
+ l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ }
+ }
+ }
+ }
+ if (sel_edges) {
+ /* Only select all loops of selected edges. */
+ BMLoop *l;
+ BMEdge *e;
+ BMIter liter, eiter;
+ BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
+ bm_loop_normal_mark_indiv_do_loop(
+ l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ /* Loops actually 'have' two edges, or said otherwise, a selected edge actually selects
+ * *two* loops in each of its faces. We have to find the other one too. */
+ if (BM_vert_in_edge(e, l->next->v)) {
+ bm_loop_normal_mark_indiv_do_loop(
+ l->next, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ }
+ else {
+ BLI_assert(BM_vert_in_edge(e, l->prev->v));
+ bm_loop_normal_mark_indiv_do_loop(
+ l->prev, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ }
+ }
+ }
+ }
+ }
+ if (sel_verts) {
+ /* Select all loops of selected verts. */
+ BMLoop *l;
+ BMVert *v;
+ BMIter liter, viter;
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ bm_loop_normal_mark_indiv_do_loop(
+ l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ }
+ }
+ }
+ }
+ }
+
+ return totloopsel;
+}
+
+static void loop_normal_editdata_init(
+ BMesh *bm, BMLoopNorEditData *lnor_ed, BMVert *v, BMLoop *l, const int offset)
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+ BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
+
+ const int l_index = BM_elem_index_get(l);
+ short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, offset);
+
+ lnor_ed->loop_index = l_index;
+ lnor_ed->loop = l;
+
+ float custom_normal[3];
+ BKE_lnor_space_custom_data_to_normal(
+ bm->lnor_spacearr->lspacearr[l_index], clnors_data, custom_normal);
+
+ lnor_ed->clnors_data = clnors_data;
+ copy_v3_v3(lnor_ed->nloc, custom_normal);
+ copy_v3_v3(lnor_ed->niloc, custom_normal);
+
+ lnor_ed->loc = v->co;
+}
+
+BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
+ const bool do_all_loops_of_vert)
+{
+ BMLoop *l;
+ BMVert *v;
+ BMIter liter, viter;
+
+ int totloopsel = 0;
+
+ BLI_assert(bm->spacearr_dirty == 0);
+
+ BMLoopNorEditDataArray *lnors_ed_arr = MEM_callocN(sizeof(*lnors_ed_arr), __func__);
+ lnors_ed_arr->lidx_to_lnor_editdata = MEM_callocN(
+ sizeof(*lnors_ed_arr->lidx_to_lnor_editdata) * bm->totloop, __func__);
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
+ }
+ const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BLI_bitmap *loops = BLI_BITMAP_NEW(bm->totloop, __func__);
+
+ /* This function define loop normals to edit, based on selection modes and history. */
+ totloopsel = bm_loop_normal_mark_indiv(bm, loops, do_all_loops_of_vert);
+
+ if (totloopsel) {
+ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(
+ sizeof(*lnor_ed) * totloopsel, __func__);
+
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ if (BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
+ loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
+ lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
+ lnor_ed++;
+ }
+ }
+ }
+ lnors_ed_arr->totloop = totloopsel;
+ }
+
+ MEM_freeN(loops);
+ lnors_ed_arr->cd_custom_normal_offset = cd_custom_normal_offset;
+ return lnors_ed_arr;
+}
+
+void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
+{
+ MEM_SAFE_FREE(lnors_ed_arr->lnor_editdata);
+ MEM_SAFE_FREE(lnors_ed_arr->lidx_to_lnor_editdata);
+ MEM_freeN(lnors_ed_arr);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Custom Normals / Vector Layer Conversion
+ * \{ */
+
+/**
+ * \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
+ * take care to run this before setting up tags.
+ */
+bool BM_custom_loop_normals_to_vector_layer(BMesh *bm)
+{
+ BMFace *f;
+ BMLoop *l;
+ BMIter liter, fiter;
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ return false;
+ }
+
+ BM_lnorspace_update(bm);
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ /* Create a loop normal layer. */
+ if (!CustomData_has_layer(&bm->ldata, CD_NORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_NORMAL);
+
+ CustomData_set_layer_flag(&bm->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
+ }
+
+ const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+ const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ const int l_index = BM_elem_index_get(l);
+ const short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, cd_custom_normal_offset);
+ float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_normal_offset);
+
+ BKE_lnor_space_custom_data_to_normal(
+ bm->lnor_spacearr->lspacearr[l_index], clnors_data, normal);
+ }
+ }
+
+ return true;
+}
+
+void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
+{
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL) ||
+ !CustomData_has_layer(&bm->ldata, CD_NORMAL)) {
+ return;
+ }
+
+ const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+ const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL);
+
+ if (bm->lnor_spacearr == NULL) {
+ bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
+ }
+
+ bm_mesh_loops_custom_normals_set(bm,
+ NULL,
+ NULL,
+ NULL,
+ bm->lnor_spacearr,
+ NULL,
+ cd_custom_normal_offset,
+ NULL,
+ cd_normal_offset,
+ add_sharp_edges);
+
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+}
+
+/** \} */
diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.h b/source/blender/bmesh/intern/bmesh_mesh_normals.h
new file mode 100644
index 00000000000..41191340e9e
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_normals.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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bmesh
+ */
+
+#include "bmesh_class.h"
+
+void BM_mesh_normals_update(BMesh *bm);
+void BM_mesh_normals_update_with_partial(BMesh *bm, const struct BMPartialUpdate *bmpinfo);
+
+void BM_verts_calc_normal_vcos(BMesh *bm,
+ const float (*fnos)[3],
+ const float (*vcos)[3],
+ float (*vnos)[3]);
+void BM_loops_calc_normal_vcos(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ const bool use_split_normals,
+ const float split_angle,
+ float (*r_lnos)[3],
+ struct MLoopNorSpaceArray *r_lnors_spacearr,
+ short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild);
+
+bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr);
+void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3]);
+void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all);
+void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor);
+void BM_lnorspace_update(BMesh *bm);
+void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges);
+#ifndef NDEBUG
+void BM_lnorspace_err(BMesh *bm);
+#endif
+
+/* Loop Generics */
+struct BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
+ const bool do_all_loops_of_vert);
+void BM_loop_normal_editdata_array_free(struct BMLoopNorEditDataArray *lnors_ed_arr);
+
+bool BM_custom_loop_normals_to_vector_layer(struct BMesh *bm);
+void BM_custom_loop_normals_from_vector_layer(struct BMesh *bm, bool add_sharp_edges);
+
+void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c
new file mode 100644
index 00000000000..7b01b61d4fa
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c
@@ -0,0 +1,254 @@
+/*
+ * 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 bmesh
+ *
+ * Generate data needed for partially updating mesh information.
+ * Currently this is used for normals and tessellation.
+ *
+ * Transform is the obvious use case where there is no need to update normals or tessellation
+ * for geometry which has not been modified.
+ *
+ * In the future this could be integrated into GPU updates too.
+ *
+ * Potential Improvements
+ * ======================
+ *
+ * Some calculations could be significantly limited in the case of affine transformations
+ * (tessellation is an obvious candidate). Where only faces which have a mix
+ * of tagged and untagged vertices would need to be recalculated.
+ *
+ * In general this would work well besides some corner cases such as scaling to zero.
+ * Although the exact result may depend on the normal (for N-GONS),
+ * so for now update the tessellation of all tagged geometry.
+ */
+
+#include "DNA_object_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_bitmap.h"
+#include "BLI_math_vector.h"
+
+#include "bmesh.h"
+
+/**
+ * Grow by 1.5x (rounding up).
+ *
+ * \note Use conservative reallocation since the initial sizes reserved
+ * may be close to (or exactly) the number of elements needed.
+ */
+#define GROW(len_alloc) ((len_alloc) + ((len_alloc) - ((len_alloc) / 2)))
+#define GROW_ARRAY(mem, len_alloc) \
+ { \
+ mem = MEM_reallocN(mem, (sizeof(*mem)) * ((len_alloc) = GROW(len_alloc))); \
+ } \
+ ((void)0)
+
+#define GROW_ARRAY_AS_NEEDED(mem, len_alloc, index) \
+ if (UNLIKELY(len_alloc == index)) { \
+ GROW_ARRAY(mem, len_alloc); \
+ }
+
+BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo,
+ BLI_bitmap *verts_tag,
+ BMVert *v)
+{
+ const int i = BM_elem_index_get(v);
+ if (!BLI_BITMAP_TEST(verts_tag, i)) {
+ BLI_BITMAP_ENABLE(verts_tag, i);
+ GROW_ARRAY_AS_NEEDED(bmpinfo->verts, bmpinfo->verts_len_alloc, bmpinfo->verts_len);
+ bmpinfo->verts[bmpinfo->verts_len++] = v;
+ return true;
+ }
+ return false;
+}
+
+BLI_INLINE bool partial_elem_edge_ensure(BMPartialUpdate *bmpinfo,
+ BLI_bitmap *edges_tag,
+ BMEdge *e)
+{
+ const int i = BM_elem_index_get(e);
+ if (!BLI_BITMAP_TEST(edges_tag, i)) {
+ BLI_BITMAP_ENABLE(edges_tag, i);
+ GROW_ARRAY_AS_NEEDED(bmpinfo->edges, bmpinfo->edges_len_alloc, bmpinfo->edges_len);
+ bmpinfo->edges[bmpinfo->edges_len++] = e;
+ return true;
+ }
+ return false;
+}
+
+BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo,
+ BLI_bitmap *faces_tag,
+ BMFace *f)
+{
+ const int i = BM_elem_index_get(f);
+ if (!BLI_BITMAP_TEST(faces_tag, i)) {
+ BLI_BITMAP_ENABLE(faces_tag, i);
+ GROW_ARRAY_AS_NEEDED(bmpinfo->faces, bmpinfo->faces_len_alloc, bmpinfo->faces_len);
+ bmpinfo->faces[bmpinfo->faces_len++] = f;
+ return true;
+ }
+ return false;
+}
+
+BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm,
+ const BMPartialUpdate_Params *params,
+ const int verts_len,
+ bool (*filter_fn)(BMVert *, void *user_data),
+ void *user_data)
+{
+ /* The caller is doing something wrong if this isn't the case. */
+ BLI_assert(verts_len <= bm->totvert);
+
+ BMPartialUpdate *bmpinfo = MEM_callocN(sizeof(*bmpinfo), __func__);
+
+ /* Reserve more edges than vertices since it's common for a grid topology
+ * to use around twice as many edges as vertices. */
+ const int default_verts_len_alloc = verts_len;
+ const int default_edges_len_alloc = min_ii(bm->totedge, verts_len * 2);
+ const int default_faces_len_alloc = min_ii(bm->totface, verts_len);
+
+ /* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags.
+ * Further, walking over all geometry to clear the tags isn't so efficient. */
+ BLI_bitmap *verts_tag = NULL;
+ BLI_bitmap *edges_tag = NULL;
+ BLI_bitmap *faces_tag = NULL;
+
+ /* Set vert inline. */
+ BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE));
+
+ if (params->do_normals || params->do_tessellate) {
+ /* - Extend to all vertices connected faces:
+ * In the case of tessellation this is enough.
+ *
+ * In the case of vertex normal calculation,
+ * All the relevant connectivity data can be accessed from the faces
+ * (there is no advantage in storing connected edges or vertices in this pass).
+ *
+ * NOTE: In the future it may be useful to differentiate between vertices
+ * that are directly marked (by the filter function when looping over all vertices).
+ * And vertices marked from indirect connections.
+ * This would require an extra tag array, so avoid this unless it's needed.
+ */
+
+ /* Faces. */
+ if (bmpinfo->faces == NULL) {
+ bmpinfo->faces_len_alloc = default_faces_len_alloc;
+ bmpinfo->faces = MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__);
+ faces_tag = BLI_BITMAP_NEW((size_t)bm->totface, __func__);
+ }
+
+ BMVert *v;
+ BMIter iter;
+ int i;
+ BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ BM_elem_index_set(v, i); /* set_inline */
+ if (!filter_fn(v, user_data)) {
+ continue;
+ }
+ BMEdge *e_iter = v->e;
+ if (e_iter != NULL) {
+ /* Loop over edges. */
+ BMEdge *e_first = v->e;
+ do {
+ BMLoop *l_iter = e_iter->l;
+ if (e_iter->l != NULL) {
+ BMLoop *l_first = e_iter->l;
+ /* Loop over radial loops. */
+ do {
+ if (l_iter->v == v) {
+ partial_elem_face_ensure(bmpinfo, faces_tag, l_iter->f);
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+ }
+ }
+ }
+
+ if (params->do_normals) {
+ /* - Extend to all faces vertices:
+ * Any changes to the faces normal needs to update all surrounding vertices.
+ *
+ * - Extend to all these vertices connected edges:
+ * These and needed to access those vertices edge vectors in normal calculation logic.
+ */
+
+ /* Vertices. */
+ if (bmpinfo->verts == NULL) {
+ bmpinfo->verts_len_alloc = default_verts_len_alloc;
+ bmpinfo->verts = MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__);
+ verts_tag = BLI_BITMAP_NEW((size_t)bm->totvert, __func__);
+ }
+
+ /* Edges. */
+ if (bmpinfo->edges == NULL) {
+ bmpinfo->edges_len_alloc = default_edges_len_alloc;
+ bmpinfo->edges = MEM_mallocN((sizeof(BMEdge *) * bmpinfo->edges_len_alloc), __func__);
+ edges_tag = BLI_BITMAP_NEW((size_t)bm->totedge, __func__);
+ }
+
+ for (int i = 0; i < bmpinfo->faces_len; i++) {
+ BMFace *f = bmpinfo->faces[i];
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ if (!partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v)) {
+ continue;
+ }
+ BMVert *v = l_iter->v;
+ BMEdge *e_first = v->e;
+ BMEdge *e_iter = e_first;
+ do {
+ if (e_iter->l) {
+ partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+
+ if (verts_tag) {
+ MEM_freeN(verts_tag);
+ }
+ if (edges_tag) {
+ MEM_freeN(edges_tag);
+ }
+ if (faces_tag) {
+ MEM_freeN(faces_tag);
+ }
+
+ bmpinfo->params = *params;
+
+ return bmpinfo;
+}
+
+void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo)
+{
+ if (bmpinfo->verts) {
+ MEM_freeN(bmpinfo->verts);
+ }
+ if (bmpinfo->edges) {
+ MEM_freeN(bmpinfo->edges);
+ }
+ if (bmpinfo->faces) {
+ MEM_freeN(bmpinfo->faces);
+ }
+ MEM_freeN(bmpinfo);
+}
diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.h b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h
new file mode 100644
index 00000000000..b31ec127744
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h
@@ -0,0 +1,64 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bmesh
+ */
+
+#include "BLI_compiler_attrs.h"
+
+/**
+ * Parameters used to determine which kinds of data needs to be generated.
+ */
+typedef struct BMPartialUpdate_Params {
+ bool do_normals;
+ bool do_tessellate;
+} BMPartialUpdate_Params;
+
+/**
+ * Cached data to speed up partial updates.
+ *
+ * Hints:
+ *
+ * - Avoid creating this data for single updates,
+ * it should be created and reused across multiple updates to gain a significant benefit
+ * (while transforming geometry for example).
+ *
+ * - Partial normal updates use face & loop indices,
+ * setting them to dirty values between updates will slow down normal recalculation.
+ */
+typedef struct BMPartialUpdate {
+ BMVert **verts;
+ BMEdge **edges;
+ BMFace **faces;
+ int verts_len, verts_len_alloc;
+ int edges_len, edges_len_alloc;
+ int faces_len, faces_len_alloc;
+
+ /** Store the parameters used in creation so invalid use can be asserted. */
+ BMPartialUpdate_Params params;
+} BMPartialUpdate;
+
+BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm,
+ const BMPartialUpdate_Params *params,
+ const int verts_len,
+ bool (*filter_fn)(BMVert *, void *user_data),
+ void *user_data)
+ ATTR_NONNULL(1, 2, 4) ATTR_WARN_UNUSED_RESULT;
+
+void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo) ATTR_NONNULL(1);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
new file mode 100644
index 00000000000..7a95e52ce25
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
@@ -0,0 +1,457 @@
+/*
+ * 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 bmesh
+ *
+ * This file contains code for polygon tessellation
+ * (creating triangles from polygons).
+ */
+
+#include "DNA_meshdata_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_heap.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+#include "BLI_memarena.h"
+#include "BLI_polyfill_2d.h"
+#include "BLI_polyfill_2d_beautify.h"
+#include "BLI_task.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+/**
+ * On systems with 32+ cores,
+ * only a very small number of faces has any advantage single threading (in the 100's).
+ * Note that between 500-2000 quads, the difference isn't so much
+ * (tessellation isn't a bottleneck in this case anyway).
+ * Avoid the slight overhead of using threads in this case.
+ */
+#define BM_FACE_TESSELLATE_THREADED_LIMIT 1024
+
+/* -------------------------------------------------------------------- */
+/** \name Default Mesh Tessellation
+ * \{ */
+
+static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3],
+ BMFace *efa,
+ MemArena **pf_arena_p)
+{
+ switch (efa->len) {
+ case 3: {
+ /* `0 1 2` -> `0 1 2` */
+ BMLoop *l;
+ BMLoop **l_ptr = looptris[0];
+ l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
+ l_ptr[1] = l = l->next;
+ l_ptr[2] = l->next;
+ return 1;
+ }
+ case 4: {
+ /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */
+ BMLoop *l;
+ BMLoop **l_ptr_a = looptris[0];
+ BMLoop **l_ptr_b = looptris[1];
+ (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa));
+ (l_ptr_a[1] = l = l->next);
+ (l_ptr_a[2] = l_ptr_b[1] = l = l->next);
+ (l_ptr_b[2] = l->next);
+
+ if (UNLIKELY(is_quad_flip_v3_first_third_fast(
+ l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) {
+ /* Flip out of degenerate 0-2 state. */
+ l_ptr_a[2] = l_ptr_b[2];
+ l_ptr_b[0] = l_ptr_a[1];
+ }
+ return 2;
+ }
+ default: {
+ BMLoop *l_iter, *l_first;
+ BMLoop **l_arr;
+
+ float axis_mat[3][3];
+ float(*projverts)[2];
+ uint(*tris)[3];
+
+ const int tris_len = efa->len - 2;
+
+ MemArena *pf_arena = *pf_arena_p;
+ if (UNLIKELY(pf_arena == NULL)) {
+ pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+ }
+
+ tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len);
+ l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len);
+ projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len);
+
+ axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
+
+ int i = 0;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ l_arr[i] = l_iter;
+ mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
+ i++;
+ } while ((l_iter = l_iter->next) != l_first);
+
+ BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
+
+ for (i = 0; i < tris_len; i++) {
+ BMLoop **l_ptr = looptris[i];
+ uint *tri = tris[i];
+
+ l_ptr[0] = l_arr[tri[0]];
+ l_ptr[1] = l_arr[tri[1]];
+ l_ptr[2] = l_arr[tri[2]];
+ }
+
+ BLI_memarena_clear(pf_arena);
+ return tris_len;
+ }
+ }
+}
+
+/**
+ * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh
+ * \param looptris:
+ *
+ * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count
+ */
+static void bm_mesh_calc_tessellation__single_threaded(BMesh *bm, BMLoop *(*looptris)[3])
+{
+#ifndef NDEBUG
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+#endif
+
+ BMIter iter;
+ BMFace *efa;
+ int i = 0;
+
+ MemArena *pf_arena = NULL;
+
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BLI_assert(efa->len >= 3);
+ i += mesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena);
+ }
+
+ if (pf_arena) {
+ BLI_memarena_free(pf_arena);
+ pf_arena = NULL;
+ }
+
+ BLI_assert(i <= looptris_tot);
+}
+
+struct TessellationUserTLS {
+ MemArena *pf_arena;
+};
+
+static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata,
+ MempoolIterData *mp_f,
+ const TaskParallelTLS *__restrict tls)
+{
+ struct TessellationUserTLS *tls_data = tls->userdata_chunk;
+ BMLoop *(*looptris)[3] = userdata;
+ BMFace *f = (BMFace *)mp_f;
+ BMLoop *l = BM_FACE_FIRST_LOOP(f);
+ const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
+ mesh_calc_tessellation_for_face(looptris + offset, f, &tls_data->pf_arena);
+}
+
+static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata),
+ void *__restrict tls_v)
+{
+ struct TessellationUserTLS *tls_data = tls_v;
+ if (tls_data->pf_arena) {
+ BLI_memarena_free(tls_data->pf_arena);
+ }
+}
+
+static void bm_mesh_calc_tessellation__multi_threaded(BMesh *bm, BMLoop *(*looptris)[3])
+{
+ BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE);
+
+ TaskParallelSettings settings;
+ struct TessellationUserTLS tls_dummy = {NULL};
+ BLI_parallel_mempool_settings_defaults(&settings);
+ settings.userdata_chunk = &tls_dummy;
+ settings.userdata_chunk_size = sizeof(tls_dummy);
+ settings.func_free = mesh_calc_tessellation_for_face_free_fn;
+ BM_iter_parallel(bm, BM_FACES_OF_MESH, mesh_calc_tessellation_for_face_fn, looptris, &settings);
+}
+
+void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3])
+{
+ if (bm->totface < BM_FACE_TESSELLATE_THREADED_LIMIT) {
+ bm_mesh_calc_tessellation__single_threaded(bm, looptris);
+ }
+ else {
+ bm_mesh_calc_tessellation__multi_threaded(bm, looptris);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Default Tessellation (Partial Updates)
+ * \{ */
+
+struct PartialTessellationUserData {
+ BMFace **faces;
+ BMLoop *(*looptris)[3];
+};
+
+struct PartialTessellationUserTLS {
+ MemArena *pf_arena;
+};
+
+static void mesh_calc_tessellation_for_face_partial_fn(void *__restrict userdata,
+ const int index,
+ const TaskParallelTLS *__restrict tls)
+{
+ struct PartialTessellationUserTLS *tls_data = tls->userdata_chunk;
+ struct PartialTessellationUserData *data = userdata;
+ BMFace *f = data->faces[index];
+ BMLoop *l = BM_FACE_FIRST_LOOP(f);
+ const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
+ mesh_calc_tessellation_for_face(data->looptris + offset, f, &tls_data->pf_arena);
+}
+
+static void mesh_calc_tessellation_for_face_partial_free_fn(
+ const void *__restrict UNUSED(userdata), void *__restrict tls_v)
+{
+ struct PartialTessellationUserTLS *tls_data = tls_v;
+ if (tls_data->pf_arena) {
+ BLI_memarena_free(tls_data->pf_arena);
+ }
+}
+
+static void bm_mesh_calc_tessellation_with_partial__multi_threaded(BMLoop *(*looptris)[3],
+ const BMPartialUpdate *bmpinfo)
+{
+ const int faces_len = bmpinfo->faces_len;
+ BMFace **faces = bmpinfo->faces;
+
+ struct PartialTessellationUserData data = {
+ .faces = faces,
+ .looptris = looptris,
+ };
+ struct PartialTessellationUserTLS tls_dummy = {NULL};
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = true;
+ settings.userdata_chunk = &tls_dummy;
+ settings.userdata_chunk_size = sizeof(tls_dummy);
+ settings.func_free = mesh_calc_tessellation_for_face_partial_free_fn;
+
+ BLI_task_parallel_range(
+ 0, faces_len, &data, mesh_calc_tessellation_for_face_partial_fn, &settings);
+}
+
+static void bm_mesh_calc_tessellation_with_partial__single_threaded(BMLoop *(*looptris)[3],
+ const BMPartialUpdate *bmpinfo)
+{
+ const int faces_len = bmpinfo->faces_len;
+ BMFace **faces = bmpinfo->faces;
+
+ MemArena *pf_arena = NULL;
+
+ for (int index = 0; index < faces_len; index++) {
+ BMFace *f = faces[index];
+ BMLoop *l = BM_FACE_FIRST_LOOP(f);
+ const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
+ mesh_calc_tessellation_for_face(looptris + offset, f, &pf_arena);
+ }
+
+ if (pf_arena) {
+ BLI_memarena_free(pf_arena);
+ }
+}
+
+void BM_mesh_calc_tessellation_with_partial(BMesh *bm,
+ BMLoop *(*looptris)[3],
+ const BMPartialUpdate *bmpinfo)
+{
+ BLI_assert(bmpinfo->params.do_tessellate);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE);
+
+ if (bmpinfo->faces_len < BM_FACE_TESSELLATE_THREADED_LIMIT) {
+ bm_mesh_calc_tessellation_with_partial__single_threaded(looptris, bmpinfo);
+ }
+ else {
+ bm_mesh_calc_tessellation_with_partial__multi_threaded(looptris, bmpinfo);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Beauty Mesh Tessellation
+ *
+ * Avoid degenerate triangles.
+ * \{ */
+
+static int mesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3],
+ BMFace *efa,
+ MemArena **pf_arena_p,
+ Heap **pf_heap_p)
+{
+ switch (efa->len) {
+ case 3: {
+ BMLoop *l;
+ BMLoop **l_ptr = looptris[0];
+ l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
+ l_ptr[1] = l = l->next;
+ l_ptr[2] = l->next;
+ return 1;
+ }
+ case 4: {
+ BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa);
+ BMLoop *l_v2 = l_v1->next;
+ BMLoop *l_v3 = l_v2->next;
+ BMLoop *l_v4 = l_v1->prev;
+
+ /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need!
+ * It's meant for rotating edges, it also calculates a new normal.
+ *
+ * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal.
+ */
+#if 0
+ const bool split_13 = (BM_verts_calc_rotate_beauty(
+ l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f);
+#else
+ float axis_mat[3][3], v_quad[4][2];
+ axis_dominant_v3_to_m3(axis_mat, efa->no);
+ mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co);
+ mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co);
+ mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co);
+ mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co);
+
+ const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc(
+ v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f;
+#endif
+
+ BMLoop **l_ptr_a = looptris[0];
+ BMLoop **l_ptr_b = looptris[1];
+ if (split_13) {
+ l_ptr_a[0] = l_v1;
+ l_ptr_a[1] = l_v2;
+ l_ptr_a[2] = l_v3;
+
+ l_ptr_b[0] = l_v1;
+ l_ptr_b[1] = l_v3;
+ l_ptr_b[2] = l_v4;
+ }
+ else {
+ l_ptr_a[0] = l_v1;
+ l_ptr_a[1] = l_v2;
+ l_ptr_a[2] = l_v4;
+
+ l_ptr_b[0] = l_v2;
+ l_ptr_b[1] = l_v3;
+ l_ptr_b[2] = l_v4;
+ }
+ return 2;
+ }
+ default: {
+ MemArena *pf_arena = *pf_arena_p;
+ Heap *pf_heap = *pf_heap_p;
+ if (UNLIKELY(pf_arena == NULL)) {
+ pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+ pf_heap = *pf_heap_p = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE);
+ }
+
+ BMLoop *l_iter, *l_first;
+ BMLoop **l_arr;
+
+ float axis_mat[3][3];
+ float(*projverts)[2];
+ uint(*tris)[3];
+
+ const int tris_len = efa->len - 2;
+
+ tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len);
+ l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len);
+ projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len);
+
+ axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
+
+ int i = 0;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ l_arr[i] = l_iter;
+ mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
+ i++;
+ } while ((l_iter = l_iter->next) != l_first);
+
+ BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
+
+ BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap);
+
+ for (i = 0; i < tris_len; i++) {
+ BMLoop **l_ptr = looptris[i];
+ uint *tri = tris[i];
+
+ l_ptr[0] = l_arr[tri[0]];
+ l_ptr[1] = l_arr[tri[1]];
+ l_ptr[2] = l_arr[tri[2]];
+ }
+
+ BLI_memarena_clear(pf_arena);
+
+ return tris_len;
+ }
+ }
+}
+
+/**
+ * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles.
+ */
+void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3])
+{
+#ifndef NDEBUG
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+#endif
+
+ BMIter iter;
+ BMFace *efa;
+ int i = 0;
+
+ MemArena *pf_arena = NULL;
+
+ /* use_beauty */
+ Heap *pf_heap = NULL;
+
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BLI_assert(efa->len >= 3);
+ i += mesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap);
+ }
+
+ if (pf_arena) {
+ BLI_memarena_free(pf_arena);
+
+ BLI_heap_free(pf_heap, NULL);
+ }
+
+ BLI_assert(i <= looptris_tot);
+}
+
+/** \} */
diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h
new file mode 100644
index 00000000000..f68a91cb988
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h
@@ -0,0 +1,30 @@
+/*
+ * 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 bmesh
+ */
+
+struct BMPartialUpdate;
+
+void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]);
+void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]);
+
+void BM_mesh_calc_tessellation_with_partial(BMesh *bm,
+ BMLoop *(*looptris)[3],
+ const struct BMPartialUpdate *bmpinfo);
diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c
index 8e5ed9c3bf0..76e32667804 100644
--- a/source/blender/bmesh/intern/bmesh_mods.c
+++ b/source/blender/bmesh/intern/bmesh_mods.c
@@ -72,7 +72,7 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v)
}
if (!v->e->l) {
if (len == 2) {
- return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
+ return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
}
/* used to kill the vertex here, but it may be connected to faces.
* so better do nothing */
@@ -82,7 +82,7 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v)
}
if (len == 2 && BM_vert_face_count_is_equal(v, 1)) {
/* boundary vertex on a face */
- return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
+ return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
}
return BM_disk_dissolve(bm, v);
}
@@ -133,7 +133,7 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
if (UNLIKELY(!BM_faces_join_pair(bm, e->l, e->l->radial_next, true))) {
return false;
}
- if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true))) {
+ if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true, true))) {
return false;
}
#endif
@@ -141,7 +141,7 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
}
if (keepedge == NULL && len == 2) {
/* collapse the vertex */
- e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true);
+ e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true, true);
if (!e) {
return false;
@@ -184,7 +184,8 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
/* collapse the vertex */
/* note, the baseedge can be a boundary of manifold, use this as join_faces arg */
- e = BM_vert_collapse_faces(bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true);
+ e = BM_vert_collapse_faces(
+ bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true, true);
if (!e) {
return false;
@@ -432,7 +433,8 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
float fac,
const bool do_del,
const bool join_faces,
- const bool kill_degenerate_faces)
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces)
{
BMEdge *e_new = NULL;
BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
@@ -503,7 +505,8 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
/* same as BM_vert_collapse_edge() however we already
* have vars to perform this operation so don't call. */
e_new = bmesh_kernel_join_edge_kill_vert(
- bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
+ bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, kill_duplicate_faces);
+
/* e_new = BM_edge_exists(tv, tv2); */ /* same as return above */
}
@@ -517,8 +520,12 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
*
* \return The New Edge
*/
-BMEdge *BM_vert_collapse_edge(
- BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
+BMEdge *BM_vert_collapse_edge(BMesh *bm,
+ BMEdge *e_kill,
+ BMVert *v_kill,
+ const bool do_del,
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces)
{
/* nice example implementation but we want loops to have their customdata
* accounted for */
@@ -546,7 +553,8 @@ BMEdge *BM_vert_collapse_edge(
#else
/* with these args faces are never joined, same as above
* but account for loop customdata */
- return BM_vert_collapse_faces(bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces);
+ return BM_vert_collapse_faces(
+ bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces, kill_duplicate_faces);
#endif
}
diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h
index 7491c309754..4328187b95e 100644
--- a/source/blender/bmesh/intern/bmesh_mods.h
+++ b/source/blender/bmesh/intern/bmesh_mods.h
@@ -51,12 +51,14 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
float fac,
const bool do_del,
const bool join_faces,
- const bool kill_degenerate_faces);
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces);
BMEdge *BM_vert_collapse_edge(BMesh *bm,
BMEdge *e_kill,
BMVert *v_kill,
const bool do_del,
- const bool kill_degenerate_faces);
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces);
BMVert *BM_edge_collapse(BMesh *bm,
BMEdge *e_kill,
diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c
index c2421939aa8..e47fd1c035d 100644
--- a/source/blender/bmesh/intern/bmesh_operators.c
+++ b/source/blender/bmesh/intern/bmesh_operators.c
@@ -421,10 +421,10 @@ void BMO_slot_mat_set(BMOperator *op,
slot->data.p = BLI_memarena_alloc(op->arena, sizeof(float[4][4]));
if (size == 4) {
- copy_m4_m4(slot->data.p, (float(*)[4])mat);
+ copy_m4_m4(slot->data.p, (const float(*)[4])mat);
}
else if (size == 3) {
- copy_m4_m3(slot->data.p, (float(*)[3])mat);
+ copy_m4_m3(slot->data.p, (const float(*)[3])mat);
}
else {
fprintf(stderr, "%s: invalid size argument %d (bmesh internal error)\n", __func__, size);
diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c
index 4ae2cc67140..5397098a7f3 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.c
+++ b/source/blender/bmesh/intern/bmesh_polygon.c
@@ -18,8 +18,7 @@
* \ingroup bmesh
*
* This file contains code for dealing
- * with polygons (normal/area calculation,
- * tessellation, etc)
+ * with polygons (normal/area calculation, tessellation, etc)
*/
#include "DNA_listBase.h"
@@ -1523,289 +1522,3 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4])
l = l->next;
r_loops[3] = l;
}
-
-/**
- * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh
- * \param looptris:
- *
- * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count
- */
-void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot)
-{
- /* use this to avoid locking pthread for _every_ polygon
- * and calling the fill function */
-#define USE_TESSFACE_SPEEDUP
-
- /* this assumes all faces can be scan-filled, which isn't always true,
- * worst case we over alloc a little which is acceptable */
-#ifndef NDEBUG
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
-#endif
-
- BMIter iter;
- BMFace *efa;
- int i = 0;
-
- MemArena *arena = NULL;
-
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- /* don't consider two-edged faces */
- if (UNLIKELY(efa->len < 3)) {
- /* do nothing */
- }
-
-#ifdef USE_TESSFACE_SPEEDUP
-
- /* no need to ensure the loop order, we know its ok */
-
- else if (efa->len == 3) {
-# if 0
- int j;
- BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) {
- looptris[i][j] = l;
- }
- i += 1;
-# else
- /* more cryptic but faster */
- BMLoop *l;
- BMLoop **l_ptr = looptris[i++];
- l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
- l_ptr[1] = l = l->next;
- l_ptr[2] = l->next;
-# endif
- }
- else if (efa->len == 4) {
-# if 0
- BMLoop *ltmp[4];
- int j;
- BLI_array_grow_items(looptris, 2);
- BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) {
- ltmp[j] = l;
- }
-
- looptris[i][0] = ltmp[0];
- looptris[i][1] = ltmp[1];
- looptris[i][2] = ltmp[2];
- i += 1;
-
- looptris[i][0] = ltmp[0];
- looptris[i][1] = ltmp[2];
- looptris[i][2] = ltmp[3];
- i += 1;
-# else
- /* more cryptic but faster */
- BMLoop *l;
- BMLoop **l_ptr_a = looptris[i++];
- BMLoop **l_ptr_b = looptris[i++];
- (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa));
- (l_ptr_a[1] = l = l->next);
- (l_ptr_a[2] = l_ptr_b[1] = l = l->next);
- (l_ptr_b[2] = l->next);
-# endif
-
- if (UNLIKELY(is_quad_flip_v3_first_third_fast(
- l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) {
- /* flip out of degenerate 0-2 state. */
- l_ptr_a[2] = l_ptr_b[2];
- l_ptr_b[0] = l_ptr_a[1];
- }
- }
-
-#endif /* USE_TESSFACE_SPEEDUP */
-
- else {
- int j;
-
- BMLoop *l_iter;
- BMLoop *l_first;
- BMLoop **l_arr;
-
- float axis_mat[3][3];
- float(*projverts)[2];
- uint(*tris)[3];
-
- const int totfilltri = efa->len - 2;
-
- if (UNLIKELY(arena == NULL)) {
- arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
- }
-
- tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri);
- l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len);
- projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len);
-
- axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
-
- j = 0;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- l_arr[j] = l_iter;
- mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co);
- j++;
- } while ((l_iter = l_iter->next) != l_first);
-
- BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena);
-
- for (j = 0; j < totfilltri; j++) {
- BMLoop **l_ptr = looptris[i++];
- uint *tri = tris[j];
-
- l_ptr[0] = l_arr[tri[0]];
- l_ptr[1] = l_arr[tri[1]];
- l_ptr[2] = l_arr[tri[2]];
- }
-
- BLI_memarena_clear(arena);
- }
- }
-
- if (arena) {
- BLI_memarena_free(arena);
- arena = NULL;
- }
-
- *r_looptris_tot = i;
-
- BLI_assert(i <= looptris_tot);
-
-#undef USE_TESSFACE_SPEEDUP
-}
-
-/**
- * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles.
- */
-void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot)
-{
- /* this assumes all faces can be scan-filled, which isn't always true,
- * worst case we over alloc a little which is acceptable */
-#ifndef NDEBUG
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
-#endif
-
- BMIter iter;
- BMFace *efa;
- int i = 0;
-
- MemArena *pf_arena = NULL;
-
- /* use_beauty */
- Heap *pf_heap = NULL;
-
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- /* don't consider two-edged faces */
- if (UNLIKELY(efa->len < 3)) {
- /* do nothing */
- }
- else if (efa->len == 3) {
- BMLoop *l;
- BMLoop **l_ptr = looptris[i++];
- l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
- l_ptr[1] = l = l->next;
- l_ptr[2] = l->next;
- }
- else if (efa->len == 4) {
- BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa);
- BMLoop *l_v2 = l_v1->next;
- BMLoop *l_v3 = l_v2->next;
- BMLoop *l_v4 = l_v1->prev;
-
- /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need!
- * It's meant for rotating edges, it also calculates a new normal.
- *
- * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal.
- */
-#if 0
- const bool split_13 = (BM_verts_calc_rotate_beauty(
- l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f);
-#else
- float axis_mat[3][3], v_quad[4][2];
- axis_dominant_v3_to_m3(axis_mat, efa->no);
- mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co);
- mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co);
- mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co);
- mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co);
-
- const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc(
- v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f;
-#endif
-
- BMLoop **l_ptr_a = looptris[i++];
- BMLoop **l_ptr_b = looptris[i++];
- if (split_13) {
- l_ptr_a[0] = l_v1;
- l_ptr_a[1] = l_v2;
- l_ptr_a[2] = l_v3;
-
- l_ptr_b[0] = l_v1;
- l_ptr_b[1] = l_v3;
- l_ptr_b[2] = l_v4;
- }
- else {
- l_ptr_a[0] = l_v1;
- l_ptr_a[1] = l_v2;
- l_ptr_a[2] = l_v4;
-
- l_ptr_b[0] = l_v2;
- l_ptr_b[1] = l_v3;
- l_ptr_b[2] = l_v4;
- }
- }
- else {
- int j;
-
- BMLoop *l_iter;
- BMLoop *l_first;
- BMLoop **l_arr;
-
- float axis_mat[3][3];
- float(*projverts)[2];
- unsigned int(*tris)[3];
-
- const int totfilltri = efa->len - 2;
-
- if (UNLIKELY(pf_arena == NULL)) {
- pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
- pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE);
- }
-
- tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri);
- l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len);
- projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len);
-
- axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
-
- j = 0;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- l_arr[j] = l_iter;
- mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co);
- j++;
- } while ((l_iter = l_iter->next) != l_first);
-
- BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
-
- BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap);
-
- for (j = 0; j < totfilltri; j++) {
- BMLoop **l_ptr = looptris[i++];
- unsigned int *tri = tris[j];
-
- l_ptr[0] = l_arr[tri[0]];
- l_ptr[1] = l_arr[tri[1]];
- l_ptr[2] = l_arr[tri[2]];
- }
-
- BLI_memarena_clear(pf_arena);
- }
- }
-
- if (pf_arena) {
- BLI_memarena_free(pf_arena);
-
- BLI_heap_free(pf_heap, NULL);
- }
-
- *r_looptris_tot = i;
-
- BLI_assert(i <= looptris_tot);
-}
diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h
index 8c2b9ee0bff..2c32cd39002 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.h
+++ b/source/blender/bmesh/intern/bmesh_polygon.h
@@ -21,12 +21,10 @@
*/
struct Heap;
+struct BMPartialUpdate;
#include "BLI_compiler_attrs.h"
-void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot);
-void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot);
-
void BM_face_calc_tessellation(const BMFace *f,
const bool use_fixed_quad,
BMLoop **r_loops,
diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
index dcf9717465c..0754564fa47 100644
--- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
+++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
@@ -106,7 +106,7 @@ static void normalize_v2_m3_v3v3(float out[2],
/**
* \note Be sure to update #bm_face_split_edgenet_find_loop_pair_exists
- * when making changed to edge picking logic.
+ * when making changes to edge picking logic.
*/
static bool bm_face_split_edgenet_find_loop_pair(BMVert *v_init,
const float face_normal[3],
@@ -456,8 +456,8 @@ static bool bm_face_split_edgenet_find_loop(BMVert *v_init,
* Splits a face into many smaller faces defined by an edge-net.
* handle customdata and degenerate cases.
*
- * - isolated holes or unsupported face configurations, will be ignored.
- * - customdata calculations aren't efficient
+ * - Isolated holes or unsupported face configurations, will be ignored.
+ * - Customdata calculations aren't efficient
* (need to calculate weights for each vert).
*/
bool BM_face_split_edgenet(BMesh *bm,
@@ -593,7 +593,7 @@ bool BM_face_split_edgenet(BMesh *bm,
BMIter iter;
BMLoop *l_other;
- /* see: #BM_loop_interp_from_face for similar logic */
+ /* See: #BM_loop_interp_from_face for similar logic. */
void **blocks = BLI_array_alloca(blocks, f->len);
float(*cos_2d)[2] = BLI_array_alloca(cos_2d, f->len);
float *w = BLI_array_alloca(w, f->len);
@@ -1064,7 +1064,7 @@ static int bm_face_split_edgenet_find_connection(const struct EdgeGroup_FindConn
#ifdef USE_PARTIAL_CONNECT
/**
- * Used to identify edges that get split off when making island from partial connection.
+ * Used to identify edges that get split off when making island from partial connection.
* fptr should be a BMFace*, but is a void* for general interface to BM_vert_separate_tested_edges
*/
static bool test_tagged_and_notface(BMEdge *e, void *fptr)
@@ -1211,7 +1211,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap,
const int v_a_index,
const int v_b_index)
{
- /* connected to eachother */
+ /* Connected to each other. */
if (UNLIKELY((remap[v_a_index] == v_b_index) || (remap[v_b_index] == v_a_index))) {
return true;
}
@@ -1226,7 +1226,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap,
* \param use_partial_connect: Support for handling islands connected by only a single edge,
* \note that this is quite slow so avoid using where possible.
* \param mem_arena: Avoids many small allocs & should be cleared after each use.
- * take care since \a r_edge_net_new is stored in \a r_edge_net_new.
+ * take care since \a edge_net_new is stored in \a r_edge_net_new.
*/
bool BM_face_split_edgenet_connect_islands(BMesh *bm,
BMFace *f,
@@ -1246,7 +1246,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
*
* Keep the first part fast since it will run very often for edge-nets that have no holes.
*
- * \note Don't use the mem_arena unless he have holes to fill.
+ * \note Don't use the mem_arena unless we have holes to fill.
* (avoid thrashing the area when the initial check isn't so intensive on the stack).
*/
@@ -1572,7 +1572,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
if (g->has_prev_edge == false) {
BMVert *v_origin = g->vert_span.min;
-
+ /* Index of BMVert for the edge group connection with `v_origin`. */
const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, false);
// BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len);
@@ -1598,7 +1598,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
{
BMVert *v_origin = g->vert_span.max;
-
+ /* Index of BMVert for the edge group connection with `v_origin`. */
const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, true);
// BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len);
@@ -1660,7 +1660,7 @@ finally:
struct TempVertPair *tvp = temp_vert_pairs.list;
do {
/* its _very_ unlikely the edge exists,
- * however splicing may case this. see: T48012 */
+ * however splicing may cause this. see: T48012 */
if (!BM_edge_exists(tvp->v_orig, tvp->v_temp)) {
BM_vert_splice(bm, tvp->v_orig, tvp->v_temp);
}
diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c
index 1c861df0150..8a0673c9b33 100644
--- a/source/blender/bmesh/operators/bmo_connect_pair.c
+++ b/source/blender/bmesh/operators/bmo_connect_pair.c
@@ -180,8 +180,8 @@ static void min_dist_dir_update(MinDistDir *dist, const float dist_dir[3])
static int state_isect_co_pair(const PathContext *pc, const float co_a[3], const float co_b[3])
{
- const float diff_a = dot_m3_v3_row_x((float(*)[3])pc->matrix, co_a) - pc->axis_sep;
- const float diff_b = dot_m3_v3_row_x((float(*)[3])pc->matrix, co_b) - pc->axis_sep;
+ const float diff_a = dot_m3_v3_row_x(pc->matrix, co_a) - pc->axis_sep;
+ const float diff_b = dot_m3_v3_row_x(pc->matrix, co_b) - pc->axis_sep;
const int test_a = (fabsf(diff_a) < CONNECT_EPS) ? 0 : (diff_a < 0.0f) ? -1 : 1;
const int test_b = (fabsf(diff_b) < CONNECT_EPS) ? 0 : (diff_b < 0.0f) ? -1 : 1;
@@ -194,7 +194,7 @@ static int state_isect_co_pair(const PathContext *pc, const float co_a[3], const
static int state_isect_co_exact(const PathContext *pc, const float co[3])
{
- const float diff = dot_m3_v3_row_x((float(*)[3])pc->matrix, co) - pc->axis_sep;
+ const float diff = dot_m3_v3_row_x(pc->matrix, co) - pc->axis_sep;
return (fabsf(diff) <= CONNECT_EPS);
}
@@ -204,8 +204,8 @@ static float state_calc_co_pair_fac(const PathContext *pc,
{
float diff_a, diff_b, diff_tot;
- diff_a = fabsf(dot_m3_v3_row_x((float(*)[3])pc->matrix, co_a) - pc->axis_sep);
- diff_b = fabsf(dot_m3_v3_row_x((float(*)[3])pc->matrix, co_b) - pc->axis_sep);
+ diff_a = fabsf(dot_m3_v3_row_x(pc->matrix, co_a) - pc->axis_sep);
+ diff_b = fabsf(dot_m3_v3_row_x(pc->matrix, co_b) - pc->axis_sep);
diff_tot = (diff_a + diff_b);
return (diff_tot > FLT_EPSILON) ? (diff_a / diff_tot) : 0.5f;
}
diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c
index da2603ad8cd..7813e30e2a8 100644
--- a/source/blender/bmesh/operators/bmo_dissolve.c
+++ b/source/blender/bmesh/operators/bmo_dissolve.c
@@ -242,7 +242,7 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op)
BM_ITER_MESH_MUTABLE (v, v_next, &viter, bm, BM_VERTS_OF_MESH) {
if (BMO_vert_flag_test(bm, v, VERT_MARK)) {
if (BM_vert_is_edge_pair(v)) {
- BM_vert_collapse_edge(bm, v->e, v, true, true);
+ BM_vert_collapse_edge(bm, v->e, v, true, true, true);
}
}
}
@@ -327,6 +327,10 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op)
/* join faces */
f_new = BM_faces_join_pair(bm, l_a, l_b, false);
+ if (f_new && BM_face_find_double(f_new)) {
+ BM_face_kill(bm, f_new);
+ f_new = NULL;
+ }
if (f_new) {
/* maintain active face */
@@ -355,7 +359,7 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op)
BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
if (BMO_vert_flag_test(bm, v, VERT_MARK)) {
if (BM_vert_is_edge_pair(v)) {
- BM_vert_collapse_edge(bm, v->e, v, true, true);
+ BM_vert_collapse_edge(bm, v->e, v, true, true, true);
}
}
}
@@ -441,10 +445,16 @@ void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op)
/* join faces */
f_new = BM_faces_join_pair(bm, l_a, l_b, false);
+ if (f_new && BM_face_find_double(f_new)) {
+ BM_face_kill(bm, f_new);
+ f_new = NULL;
+ }
- /* maintain active face */
- if (act_face && bm->act_face == NULL) {
- bm->act_face = f_new;
+ if (f_new) {
+ /* maintain active face */
+ if (act_face && bm->act_face == NULL) {
+ bm->act_face = f_new;
+ }
}
}
}
@@ -462,7 +472,7 @@ void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op)
/* final cleanup */
BMO_ITER (v, &oiter, op->slots_in, "verts", BM_VERT) {
if (BM_vert_is_edge_pair(v)) {
- BM_vert_collapse_edge(bm, v->e, v, false, true);
+ BM_vert_collapse_edge(bm, v->e, v, false, true, true);
}
}
diff --git a/source/blender/bmesh/operators/bmo_offset_edgeloops.c b/source/blender/bmesh/operators/bmo_offset_edgeloops.c
index d723e128bf1..2c7e478b549 100644
--- a/source/blender/bmesh/operators/bmo_offset_edgeloops.c
+++ b/source/blender/bmesh/operators/bmo_offset_edgeloops.c
@@ -263,7 +263,7 @@ void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op)
}
while ((v = STACK_POP(varr))) {
- bmesh_kernel_join_edge_kill_vert(bm, v->e, v, true, false, false);
+ bmesh_kernel_join_edge_kill_vert(bm, v->e, v, true, false, false, true);
}
}
}
diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c
index a0a31ab6154..f47c8dfb405 100644
--- a/source/blender/bmesh/operators/bmo_primitive.c
+++ b/source/blender/bmesh/operators/bmo_primitive.c
@@ -867,19 +867,19 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
BMIter iter;
const float axis[3] = {0, 0, 1};
float vec[3], mat[4][4], cmat[3][3];
- float phi, phid;
int a;
BMO_slot_mat4_get(op->slots_in, "matrix", mat);
- phid = 2.0f * (float)M_PI / tot;
+ const float phid = (float)M_PI / tot;
/* phi = 0.25f * (float)M_PI; */ /* UNUSED */
/* one segment first */
- phi = 0;
- phid /= 2;
for (a = 0; a <= tot; a++) {
/* Going in this direction, then edge extruding, makes normals face outward */
+ /* Calculate with doubles for higher precision, see: T87779. */
+ const float phi = M_PI * ((double)a / (double)tot);
+
vec[0] = 0.0;
vec[1] = dia * sinf(phi);
vec[2] = dia * cosf(phi);
@@ -891,7 +891,6 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
BMO_edge_flag_enable(bm, e, EDGE_ORIG);
}
- phi += phid;
preveve = eve;
}
@@ -1272,7 +1271,7 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");
BMVert *v1, *lastv1 = NULL, *cent1, *firstv1 = NULL;
- float vec[3], mat[4][4], phi, phid;
+ float vec[3], mat[4][4];
int a;
if (!segs) {
@@ -1281,9 +1280,6 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
BMO_slot_mat4_get(op->slots_in, "matrix", mat);
- phid = 2.0f * (float)M_PI / segs;
- phi = 0;
-
if (cap_ends) {
zero_v3(vec);
mul_m4_v3(mat, vec);
@@ -1292,8 +1288,11 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
BMO_vert_flag_enable(bm, cent1, VERT_MARK);
}
- for (a = 0; a < segs; a++, phi += phid) {
+ for (a = 0; a < segs; a++) {
/* Going this way ends up with normal(s) upward */
+
+ /* Calculate with doubles for higher precision, see: T87779. */
+ const float phi = (2.0 * M_PI) * ((double)a / (double)segs);
vec[0] = -radius * sinf(phi);
vec[1] = radius * cosf(phi);
vec[2] = 0.0f;
@@ -1392,7 +1391,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
{
BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2;
BMFace *f;
- float vec[3], mat[4][4], phi, phid;
+ float vec[3], mat[4][4];
const float dia1 = BMO_slot_float_get(op->slots_in, "diameter1");
const float dia2 = BMO_slot_float_get(op->slots_in, "diameter2");
const float depth_half = 0.5f * BMO_slot_float_get(op->slots_in, "depth");
@@ -1409,9 +1408,6 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
BMO_slot_mat4_get(op->slots_in, "matrix", mat);
- phid = 2.0f * (float)M_PI / segs;
- phi = 0;
-
if (cap_ends) {
vec[0] = vec[1] = 0.0f;
vec[2] = -depth_half;
@@ -1432,7 +1428,10 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
const int side_faces_len = segs - 1;
BMFace **side_faces = MEM_mallocN(sizeof(*side_faces) * side_faces_len, __func__);
- for (int i = 0; i < segs; i++, phi += phid) {
+ for (int i = 0; i < segs; i++) {
+ /* Calculate with doubles for higher precision, see: T87779. */
+ const float phi = (2.0 * M_PI) * ((double)i / (double)segs);
+
vec[0] = dia1 * sinf(phi);
vec[1] = dia1 * cosf(phi);
vec[2] = -depth_half;
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index cef97f20a6a..d39fb5e81f1 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -7361,7 +7361,6 @@ static float geometry_collide_offset(BevelParams *bp, EdgeHalf *eb)
static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea)
{
float no_collide_offset = bp->offset + 1e6;
- float limit = no_collide_offset;
if (bp->offset == 0.0f) {
return no_collide_offset;
}
@@ -7373,8 +7372,7 @@ static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea)
if (kab <= 0.0f) {
return no_collide_offset;
}
- limit = la / kab;
- return limit;
+ return la / kab;
}
/**
diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c
index 85de065daff..e7d0fe6a0c6 100644
--- a/source/blender/bmesh/tools/bmesh_bisect_plane.c
+++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c
@@ -39,12 +39,13 @@
#include "BLI_utildefines_stack.h"
#include "bmesh.h"
-#include "bmesh_bisect_plane.h" /* own include */
+#include "bmesh_bisect_plane.h" /* Own include. */
-#include "BLI_strict_flags.h" /* keep last */
+#include "BLI_strict_flags.h" /* Keep last. */
/* -------------------------------------------------------------------- */
-/* Math utils */
+/** \name Math Functions
+ * \{ */
static short plane_point_test_v3(const float plane[4],
const float co[3],
@@ -63,10 +64,15 @@ static short plane_point_test_v3(const float plane[4],
return 0;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Wrappers to hide internal data-structure abuse,
+/** \name BMesh Element Accessors
+ *
+ * Wrappers to hide internal data-structure abuse,
* later we may want to move this into some hash lookup
- * to a separate struct, but for now we can store in BMesh data */
+ * to a separate struct, but for now we can store in #BMesh data.
+ * \{ */
#define BM_VERT_DIR(v) ((short *)(&(v)->head.index))[0] /* Direction -1/0/1 */
#define BM_VERT_SKIP(v) ((short *)(&(v)->head.index))[1] /* Skip Vert 0/1 */
@@ -75,12 +81,16 @@ static short plane_point_test_v3(const float plane[4],
#define BM_VERT_LOOPINDEX(v) /* The verts index within a face (temp var) */ \
(*((uint *)(&(v)->no[2])))
-/**
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BMesh Flag Accessors
+ *
* Hide flag access
- * (for more readable code since same flag is used differently for vert/edgeface)...
+ * (for more readable code since same flag is used differently for vert/edge-face).
*/
-/* enable when vertex is in the center and its faces have been added to the stack */
+/** Enable when vertex is in the center and its faces have been added to the stack. */
BLI_INLINE void vert_is_center_enable(BMVert *v)
{
BM_elem_flag_enable(v, BM_ELEM_TAG);
@@ -94,7 +104,13 @@ BLI_INLINE bool vert_is_center_test(BMVert *v)
return (BM_elem_flag_test(v, BM_ELEM_TAG) != 0);
}
-/* enable when the edge can be cut */
+BLI_INLINE bool vert_pair_adjacent_in_orig_face(BMVert *v_a, BMVert *v_b, const uint f_len_orig)
+{
+ const uint delta = (uint)abs((int)BM_VERT_LOOPINDEX(v_a) - (int)BM_VERT_LOOPINDEX(v_b));
+ return ELEM(delta, 1, (uint)(f_len_orig - 1));
+}
+
+/** Enable when the edge can be cut. */
BLI_INLINE void edge_is_cut_enable(BMEdge *e)
{
BM_elem_flag_enable(e, BM_ELEM_TAG);
@@ -108,7 +124,7 @@ BLI_INLINE bool edge_is_cut_test(BMEdge *e)
return (BM_elem_flag_test(e, BM_ELEM_TAG) != 0);
}
-/* enable when the faces are added to the stack */
+/** Enable when the faces are added to the stack. */
BLI_INLINE void face_in_stack_enable(BMFace *f)
{
BM_elem_flag_disable(f, BM_ELEM_TAG);
@@ -122,8 +138,11 @@ BLI_INLINE bool face_in_stack_test(BMFace *f)
return (BM_elem_flag_test(f, BM_ELEM_TAG) == 0);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BMesh utils */
+/** \name BMesh Face Bisect
+ * \{ */
static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
{
@@ -142,32 +161,39 @@ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
static void bm_face_bisect_verts(
BMesh *bm, BMFace *f, const float plane[4], const short oflag_center, const short oflag_new)
{
- /* unlikely more than 2 verts are needed */
+ /* Unlikely more than 2 verts are needed. */
const uint f_len_orig = (uint)f->len;
BMVert **vert_split_arr = BLI_array_alloca(vert_split_arr, f_len_orig);
STACK_DECLARE(vert_split_arr);
BMLoop *l_iter, *l_first;
bool use_dirs[3] = {false, false, false};
bool is_inside = false;
+ /* True when the face contains one or more edges with both it's vertices on the plane.
+ * When set, centered loops are walked over to check if they need to be skipped. */
+ bool face_has_center_edge = false;
STACK_INIT(vert_split_arr, f_len_orig);
l_first = BM_FACE_FIRST_LOOP(f);
- /* add plane-aligned verts to the stack
- * and check we have verts from both sides in this face,
- * ... that the face doesn't only have boundary verts on the plane for eg. */
+ /* Add plane-aligned verts to the stack and check we have verts from both sides in this face
+ * (that the face doesn't only have boundary verts on the plane for eg). */
l_iter = l_first;
do {
if (vert_is_center_test(l_iter->v)) {
BLI_assert(BM_VERT_DIR(l_iter->v) == 0);
- /* if both are -1 or 1, or both are zero:
- * don't flip 'inside' var while walking */
+ /* If both are -1 or 1, or both are zero: don't flip 'inside' var while walking. */
BM_VERT_SKIP(l_iter->v) = (((BM_VERT_DIR(l_iter->prev->v) ^ BM_VERT_DIR(l_iter->next->v))) ==
0);
STACK_PUSH(vert_split_arr, l_iter->v);
+
+ if (face_has_center_edge == false) {
+ if (vert_is_center_test(l_iter->prev->v)) {
+ face_has_center_edge = true;
+ }
+ }
}
use_dirs[BM_VERT_DIR(l_iter->v) + 1] = true;
} while ((l_iter = l_iter->next) != l_first);
@@ -180,7 +206,7 @@ static void bm_face_bisect_verts(
l_a = BM_face_vert_share_loop(f, vert_split_arr[0]);
l_b = BM_face_vert_share_loop(f, vert_split_arr[1]);
- /* common case, just cut the face once */
+ /* Common case, just cut the face once. */
BM_face_split(bm, f, l_a, l_b, &l_new, NULL, true);
if (l_new) {
if (oflag_center | oflag_new) {
@@ -192,7 +218,63 @@ static void bm_face_bisect_verts(
}
}
else {
- /* less common case, _complicated_ we need to calculate how to do multiple cuts */
+ /* Less common case, _complicated_ we need to calculate how to do multiple cuts. */
+
+ uint i = 0;
+
+ /* ---- */
+ /* Check contiguous spans of centered vertices (skipping when necessary). */
+ if (face_has_center_edge) {
+
+ /* Loop indices need to be set for adjacency checks. */
+ l_iter = l_first;
+ do {
+ BM_VERT_LOOPINDEX(l_iter->v) = i++;
+ } while ((l_iter = l_iter->next) != l_first);
+
+ /* Start out on a non-centered vertex so a span of centered vertices can be looped over
+ * without having to scan backwards as well as forwards. */
+ BMLoop *l_first_non_center = l_first;
+ while (vert_is_center_test(l_first_non_center->v)) {
+ l_first_non_center = l_first_non_center->next;
+ }
+
+ l_iter = l_first_non_center;
+ do {
+ if (BM_VERT_SKIP(l_iter->v)) {
+ continue;
+ }
+ /* No need to check the previous as the iteration starts on a non-centered vertex. */
+ if (!(vert_is_center_test(l_iter->v) && vert_is_center_test(l_iter->next->v))) {
+ continue;
+ }
+
+ /* Walk over the next loops as long as they are centered. */
+ BMLoop *l_prev = l_iter->prev;
+ BMLoop *l_next = l_iter->next->next;
+ /* No need to scan the previous vertices,
+ * these will have been dealt with in previous steps. */
+ BLI_assert(!vert_is_center_test(l_prev->v));
+ while (vert_is_center_test(l_next->v)) {
+ l_next = l_next->next;
+ }
+
+ /* Skip all vertices when the edges connected to the beginning/end
+ * of the range are on a different side of the bisecting plane. */
+ if (!(BM_VERT_DIR(l_prev->v) ^ BM_VERT_DIR(l_next->v))) {
+ BLI_assert(!vert_is_center_test(l_prev->v));
+ l_iter = l_prev->next;
+ while (l_iter != l_next) {
+ BLI_assert(vert_is_center_test(l_iter->v));
+ BM_VERT_SKIP(l_iter->v) = true;
+ l_iter = l_iter->next;
+ }
+ }
+ /* Step over the span already handled, even if skip wasn't set. */
+ l_iter = l_next->prev;
+ } while ((l_iter = l_iter->next) != l_first_non_center);
+ }
+
float(*face_verts_proj_2d)[2] = BLI_array_alloca(face_verts_proj_2d, f_len_orig);
float axis_mat[3][3];
@@ -200,15 +282,12 @@ static void bm_face_bisect_verts(
STACK_DECLARE(face_split_arr);
float sort_dir[3];
- uint i;
/* ---- */
/* Calculate the direction to sort verts in the face intersecting the plane */
- /* exact dir isn't so important,
- * just need a dir for sorting verts across face,
- * 'sort_dir' could be flipped either way, it not important, we only need to order the array
- */
+ /* The exact direction isn't important, vertices just need to be sorted across the face.
+ * (`sort_dir` could be flipped either way). */
cross_v3_v3v3(sort_dir, f->no, plane);
if (UNLIKELY(normalize_v3(sort_dir) == 0.0f)) {
/* find any 2 verts and get their direction */
@@ -219,8 +298,8 @@ static void bm_face_bisect_verts(
}
}
if (UNLIKELY(i == STACK_SIZE(vert_split_arr))) {
- /* ok, we can't do anything useful here,
- * face has no area or so, bail out, this is highly unlikely but not impossible */
+ /* Ok, we can't do anything useful here,
+ * face has no area or so, bail out, this is highly unlikely but not impossible. */
goto finally;
}
}
@@ -228,20 +307,19 @@ static void bm_face_bisect_verts(
/* ---- */
/* Calculate 2d coords to use for intersection checks */
- /* get the faces 2d coords */
+ /* Get the faces 2d coords. */
BLI_assert(BM_face_is_normal_valid(f));
axis_dominant_v3_to_m3(axis_mat, f->no);
l_iter = l_first;
i = 0;
do {
- BM_VERT_LOOPINDEX(l_iter->v) = i;
mul_v2_m3v3(face_verts_proj_2d[i], axis_mat, l_iter->v->co);
i++;
} while ((l_iter = l_iter->next) != l_first);
/* ---- */
- /* Sort the verts across the face from one side to another */
+ /* Sort the verts across the face from one side to another. */
for (i = 0; i < STACK_SIZE(vert_split_arr); i++) {
BMVert *v = vert_split_arr[i];
BM_VERT_SORTVAL(v) = dot_v3v3(sort_dir, v->co);
@@ -251,9 +329,9 @@ static void bm_face_bisect_verts(
vert_split_arr, STACK_SIZE(vert_split_arr), sizeof(*vert_split_arr), bm_vert_sortval_cb);
/* ---- */
- /* Split the face across sorted splits */
+ /* Split the face across sorted splits. */
- /* note: we don't know which face gets which splits,
+ /* NOTE: we don't know which face gets which splits,
* so at the moment we have to search all faces for the vert pair,
* while not all that nice, typically there are < 5 resulting faces,
* so its not _that_ bad. */
@@ -265,6 +343,12 @@ static void bm_face_bisect_verts(
BMVert *v_a = vert_split_arr[i];
BMVert *v_b = vert_split_arr[i + 1];
+ if (face_has_center_edge) {
+ if (vert_pair_adjacent_in_orig_face(v_a, v_b, f_len_orig)) {
+ continue;
+ }
+ }
+
if (!BM_VERT_SKIP(v_a)) {
is_inside = !is_inside;
}
@@ -275,8 +359,8 @@ static void bm_face_bisect_verts(
uint j;
for (j = 0; j < STACK_SIZE(face_split_arr); j++) {
- /* would be nice to avoid loop lookup here,
- * but we need to know which face the verts are in */
+ /* It would be nice to avoid loop lookup here,
+ * but we need to know which face the verts are in. */
if ((l_a = BM_face_vert_share_loop(face_split_arr[j], v_a)) &&
(l_b = BM_face_vert_share_loop(face_split_arr[j], v_b))) {
found = true;
@@ -284,11 +368,10 @@ static void bm_face_bisect_verts(
}
}
- /* ideally wont happen, but it can for self intersecting faces */
+ /* Ideally wont happen, but it can for self intersecting faces. */
// BLI_assert(found == true);
- /* in fact this simple test is good enough,
- * test if the loops are adjacent */
+ /* In fact this simple test is good enough, test if the loops are adjacent. */
if (found && !BM_loop_is_adjacent(l_a, l_b)) {
BMLoop *l_new;
BMFace *f_tmp;
@@ -322,8 +405,11 @@ finally:
(void)vert_split_arr;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Main logic */
+/** \name Public BMesh Bisect Function
+ * \{ */
/**
* \param use_snap_center: Snap verts onto the plane.
@@ -350,25 +436,25 @@ void BM_mesh_bisect_plane(BMesh *bm,
BMIter iter;
if (use_tag) {
- /* build tagged edge array */
+ /* Build tagged edge array. */
BMEdge *e;
einput_len = 0;
- /* flush edge tags to verts */
+ /* Flush edge tags to verts. */
BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false);
- /* keep face tags as is */
+ /* Keep face tags as is. */
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
if (edge_is_cut_test(e)) {
edges_arr[einput_len++] = e;
- /* flush edge tags to verts */
+ /* Flush edge tags to verts. */
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
}
}
- /* face tags are set by caller */
+ /* Face tags are set by caller. */
}
else {
BMEdge *e;
@@ -388,7 +474,7 @@ void BM_mesh_bisect_plane(BMesh *bm,
if (use_tag && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
vert_is_center_disable(v);
- /* these should never be accessed */
+ /* These should never be accessed. */
BM_VERT_DIR(v) = 0;
BM_VERT_DIST(v) = 0.0f;
@@ -408,11 +494,11 @@ void BM_mesh_bisect_plane(BMesh *bm,
}
}
- /* store a stack of faces to be evaluated for splitting */
+ /* Store a stack of faces to be evaluated for splitting. */
BLI_LINKSTACK_INIT(face_stack);
for (i = 0; i < einput_len; i++) {
- /* we could check edge_is_cut_test(e) but there is no point */
+ /* We could check `edge_is_cut_test(e)` but there is no point. */
BMEdge *e = edges_arr[i];
const int side[2] = {BM_VERT_DIR(e->v1), BM_VERT_DIR(e->v2)};
const float dist[2] = {BM_VERT_DIST(e->v1), BM_VERT_DIST(e->v2)};
@@ -449,8 +535,8 @@ void BM_mesh_bisect_plane(BMesh *bm,
BM_VERT_DIST(v_new) = 0.0f;
}
else if (side[0] == 0 || side[1] == 0) {
- /* check if either edge verts are aligned,
- * if so - tag and push all faces that use it into the stack */
+ /* Check if either edge verts are aligned,
+ * if so - tag and push all faces that use it into the stack. */
uint j;
BM_ITER_ELEM_INDEX (v, &iter, e, BM_VERTS_OF_EDGE, j) {
if (side[j] == 0) {
@@ -470,7 +556,7 @@ void BM_mesh_bisect_plane(BMesh *bm,
}
}
- /* if both verts are on the center - tag it */
+ /* If both verts are on the center - tag it. */
if (oflag_center) {
if (side[0] == 0 && side[1] == 0) {
BMO_edge_flag_enable(bm, e, oflag_center);
@@ -485,9 +571,11 @@ void BM_mesh_bisect_plane(BMesh *bm,
bm_face_bisect_verts(bm, f, plane, oflag_center, oflag_new);
}
- /* Caused by access macros: BM_VERT_DIR, BM_VERT_SKIP. */
+ /* Caused by access macros: #BM_VERT_DIR, #BM_VERT_SKIP. */
bm->elem_index_dirty |= BM_VERT;
- /* now we have all faces to split in the stack */
+ /* Now we have all faces to split in the stack. */
BLI_LINKSTACK_FREE(face_stack);
}
+
+/** \} */
diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc
index fec33a04e6f..487ef6427af 100644
--- a/source/blender/bmesh/tools/bmesh_boolean.cc
+++ b/source/blender/bmesh/tools/bmesh_boolean.cc
@@ -38,7 +38,8 @@ namespace blender::meshintersect {
#ifdef WITH_GMP
-/** Make a #blender::meshintersect::Mesh from #BMesh bm.
+/**
+ * Make a #blender::meshintersect::Mesh from #BMesh bm.
* We are given a triangulation of it from the caller via #looptris,
* which are looptris_tot triples of loops that together tessellate
* the faces of bm.
diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
index 4a024f745ed..8b4a9bb26ac 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
@@ -439,7 +439,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm,
for (i = 0; i < vinput_len; i++) {
BMVert *v = vinput_arr[i];
if (LIKELY(v != NULL) && BM_vert_is_edge_pair(v)) {
- BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */
+ BM_vert_collapse_edge(bm, v->e, v, true, true, true); /* join edges */
}
}
}
@@ -482,7 +482,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm,
BM_vert_is_edge_pair(v)
#endif
) {
- e_new = BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */
+ e_new = BM_vert_collapse_edge(bm, v->e, v, true, true, true); /* join edges */
if (e_new) {
diff --git a/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c b/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
index 0a512fdd592..c96a7be1adf 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
@@ -110,7 +110,7 @@ static bool bm_vert_dissolve_fan(BMesh *bm, BMVert *v)
if (tot_edge == 2) {
/* check for 2 wire verts only */
if (tot_edge_wire == 2) {
- return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
+ return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
}
}
else if (tot_edge == 4) {
diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c
index 81b016e9601..710d7f79637 100644
--- a/source/blender/bmesh/tools/bmesh_intersect.c
+++ b/source/blender/bmesh/tools/bmesh_intersect.c
@@ -1622,7 +1622,7 @@ bool BM_mesh_intersect(BMesh *bm,
}
if (ok) {
- BM_vert_collapse_edge(bm, v->e, v, true, false);
+ BM_vert_collapse_edge(bm, v->e, v, true, false, false);
}
}
}
@@ -1660,5 +1660,9 @@ bool BM_mesh_intersect(BMesh *bm,
BLI_memarena_free(s.mem_arena);
+ /* It's unlikely the selection history is useful at this point,
+ * if this is not called this array would need to be validated, see: T86799. */
+ BM_select_history_clear(bm);
+
return (has_edit_isect || has_edit_boolean);
}
diff --git a/source/blender/bmesh/tools/bmesh_intersect.h b/source/blender/bmesh/tools/bmesh_intersect.h
index adb88f2fd76..d09ea67a3bb 100644
--- a/source/blender/bmesh/tools/bmesh_intersect.h
+++ b/source/blender/bmesh/tools/bmesh_intersect.h
@@ -20,6 +20,10 @@
* \ingroup bmesh
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
bool BM_mesh_intersect(BMesh *bm,
struct BMLoop *(*looptris)[3],
const int looptris_tot,
@@ -41,3 +45,7 @@ enum {
BMESH_ISECT_BOOLEAN_UNION = 1,
BMESH_ISECT_BOOLEAN_DIFFERENCE = 2,
};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 64033cbe5c4..ac59d832013 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -49,6 +49,8 @@ set(SRC
COM_compositor.h
COM_defines.h
+ intern/COM_BufferOperation.cc
+ intern/COM_BufferOperation.h
intern/COM_CPUDevice.cc
intern/COM_CPUDevice.h
intern/COM_ChunkOrder.cc
@@ -63,16 +65,23 @@ set(SRC
intern/COM_Debug.h
intern/COM_Device.cc
intern/COM_Device.h
+ intern/COM_Enums.cc
intern/COM_ExecutionGroup.cc
intern/COM_ExecutionGroup.h
+ intern/COM_ExecutionModel.cc
+ intern/COM_ExecutionModel.h
intern/COM_ExecutionSystem.cc
intern/COM_ExecutionSystem.h
+ intern/COM_FullFrameExecutionModel.cc
+ intern/COM_FullFrameExecutionModel.h
intern/COM_MemoryBuffer.cc
intern/COM_MemoryBuffer.h
intern/COM_MemoryProxy.cc
intern/COM_MemoryProxy.h
intern/COM_MetaData.cc
intern/COM_MetaData.h
+ intern/COM_MultiThreadedOperation.cc
+ intern/COM_MultiThreadedOperation.h
intern/COM_Node.cc
intern/COM_Node.h
intern/COM_NodeConverter.cc
@@ -85,10 +94,12 @@ set(SRC
intern/COM_NodeOperationBuilder.h
intern/COM_OpenCLDevice.cc
intern/COM_OpenCLDevice.h
+ intern/COM_SharedOperationBuffers.cc
+ intern/COM_SharedOperationBuffers.h
intern/COM_SingleThreadedOperation.cc
intern/COM_SingleThreadedOperation.h
- intern/COM_SocketReader.cc
- intern/COM_SocketReader.h
+ intern/COM_TiledExecutionModel.cc
+ intern/COM_TiledExecutionModel.h
intern/COM_WorkPackage.cc
intern/COM_WorkPackage.h
intern/COM_WorkScheduler.cc
@@ -280,6 +291,8 @@ set(SRC
nodes/COM_VectorBlurNode.h
operations/COM_VectorBlurOperation.cc
operations/COM_VectorBlurOperation.h
+ nodes/COM_AntiAliasingNode.cc
+ nodes/COM_AntiAliasingNode.h
nodes/COM_BlurNode.cc
nodes/COM_BlurNode.h
nodes/COM_BokehBlurNode.cc
@@ -296,6 +309,7 @@ set(SRC
nodes/COM_FilterNode.h
nodes/COM_InpaintNode.cc
nodes/COM_InpaintNode.h
+
operations/COM_BlurBaseOperation.cc
operations/COM_BlurBaseOperation.h
operations/COM_BokehBlurOperation.cc
@@ -320,6 +334,8 @@ set(SRC
operations/COM_MovieClipAttributeOperation.h
operations/COM_MovieDistortionOperation.cc
operations/COM_MovieDistortionOperation.h
+ operations/COM_SMAAOperation.cc
+ operations/COM_SMAAOperation.h
operations/COM_VariableSizeBokehBlurOperation.cc
operations/COM_VariableSizeBokehBlurOperation.h
@@ -568,6 +584,23 @@ data_to_c(
add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS)
+set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations)
+set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h)
+add_custom_command(
+ OUTPUT ${GENSRC}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR}
+ COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC}
+ DEPENDS smaa_areatex
+)
+add_custom_target(smaa_areatex_header
+ SOURCES ${GENSRC}
+)
+list(APPEND SRC
+ ${GENSRC}
+)
+unset(GENSRC)
+unset(GENSRC_DIR)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -586,3 +619,9 @@ if(WITH_OPENIMAGEDENOISE)
endif()
blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+if(CXX_WARN_NO_SUGGEST_OVERRIDE)
+ target_compile_options(bf_compositor PRIVATE "-Wsuggest-override")
+endif()
+
+add_dependencies(bf_compositor smaa_areatex_header)
diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h
index 8e3caf7aaf5..a7f9081f3fc 100644
--- a/source/blender/compositor/COM_compositor.h
+++ b/source/blender/compositor/COM_compositor.h
@@ -113,11 +113,11 @@ extern "C" {
*
* When the chunk-order is determined, the first few chunks will be checked if they can be scheduled.
* Chunks can have three states:
- * - [@ref eChunkExecutionState.NOT_SCHEDULED]:
+ * - [@ref eWorkPackageState.NotScheduled]:
* Chunk is not yet scheduled, or dependencies are not met.
- * - [@ref eChunkExecutionState.SCHEDULED]:
+ * - [@ref eWorkPackageState.Scheduled]:
* All dependencies are met, chunk is scheduled, but not finished.
- * - [@ref eChunkExecutionState.EXECUTED]:
+ * - [@ref eWorkPackageState.Executed]:
* Chunk is finished.
*
* \see ExecutionGroup.execute
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 266f532ebb8..5a52d216117 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -18,6 +18,18 @@
#pragma once
+namespace blender::compositor {
+
+enum class eExecutionModel {
+ /**
+ * Operations are executed from outputs to inputs grouped in execution groups and rendered
+ * in tiles.
+ */
+ Tiled,
+ /** Operations are fully rendered in order from inputs to outputs. */
+ FullFrame
+};
+
/**
* \brief possible data types for sockets
* \ingroup Model
@@ -32,37 +44,27 @@ enum class DataType {
};
/**
- * \brief Possible quality settings
- * \see CompositorContext.quality
- * \ingroup Execution
+ * Utility to get the number of channels of the given data type.
*/
-enum class CompositorQuality {
- /** \brief High quality setting */
- High = 0,
- /** \brief Medium quality setting */
- Medium = 1,
- /** \brief Low quality setting */
- Low = 2,
-};
+constexpr int COM_data_type_num_channels(const DataType datatype)
+{
+ switch (datatype) {
+ case DataType::Value:
+ return 1;
+ case DataType::Vector:
+ return 3;
+ case DataType::Color:
+ default:
+ return 4;
+ }
+}
-/**
- * \brief Possible priority settings
- * \ingroup Execution
- */
-enum class CompositorPriority {
- /** \brief High quality setting */
- High = 2,
- /** \brief Medium quality setting */
- Medium = 1,
- /** \brief Low quality setting */
- Low = 0,
-};
+constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value);
+constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color);
// configurable items
// chunk size determination
-#define COM_PREVIEW_SIZE 140.0f
-//#define COM_DEBUG
// chunk order
/**
@@ -82,10 +84,8 @@ enum class ChunkOrdering {
Default = ChunkOrdering::CenterOut,
};
-#define COM_RULE_OF_THIRDS_DIVIDER 100.0f
-
-#define COM_NUM_CHANNELS_VALUE 1
-#define COM_NUM_CHANNELS_VECTOR 3
-#define COM_NUM_CHANNELS_COLOR 4
+constexpr float COM_PREVIEW_SIZE = 140.f;
+constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
+constexpr float COM_BLUR_BOKEH_PIXELS = 512;
-#define COM_BLUR_BOKEH_PIXELS 512
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc
new file mode 100644
index 00000000000..8464d01801f
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferOperation.cc
@@ -0,0 +1,65 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_BufferOperation.h"
+
+namespace blender::compositor {
+
+BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
+{
+ buffer_ = buffer;
+ /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following
+ * code to: set_resolution(buffer.get_size()) */
+ unsigned int resolution[2];
+ resolution[0] = buffer->getWidth();
+ resolution[1] = buffer->getHeight();
+ setResolution(resolution);
+ addOutputSocket(data_type);
+}
+
+void *BufferOperation::initializeTileData(rcti * /*rect*/)
+{
+ return buffer_;
+}
+
+void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
+{
+ switch (sampler) {
+ case PixelSampler::Nearest:
+ buffer_->read(output, x, y);
+ break;
+ case PixelSampler::Bilinear:
+ default:
+ buffer_->readBilinear(output, x, y);
+ break;
+ case PixelSampler::Bicubic:
+ /* No bicubic. Same implementation as ReadBufferOperation. */
+ buffer_->readBilinear(output, x, y);
+ break;
+ }
+}
+
+void BufferOperation::executePixelFiltered(
+ float output[4], float x, float y, float dx[2], float dy[2])
+{
+ const float uv[2] = {x, y};
+ const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
+ buffer_->readEWA(output, uv, deriv);
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h
new file mode 100644
index 00000000000..f87cd4db94e
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferOperation.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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+class BufferOperation : public NodeOperation {
+ private:
+ MemoryBuffer *buffer_;
+
+ public:
+ BufferOperation(MemoryBuffer *buffer, DataType data_type);
+
+ void *initializeTileData(rcti *rect) override;
+ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc
index b520a437008..2ca5557e278 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.cc
+++ b/source/blender/compositor/intern/COM_CPUDevice.cc
@@ -18,19 +18,36 @@
#include "COM_CPUDevice.h"
+#include "COM_ExecutionGroup.h"
+
+#include "BLI_rect.h"
+
+namespace blender::compositor {
+
CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id)
{
}
-void CPUDevice::execute(WorkPackage *work)
+void CPUDevice::execute(WorkPackage *work_package)
{
- const unsigned int chunkNumber = work->chunk_number;
- ExecutionGroup *executionGroup = work->execution_group;
- rcti rect;
+ switch (work_package->type) {
+ case eWorkPackageType::Tile: {
+ const unsigned int chunkNumber = work_package->chunk_number;
+ ExecutionGroup *executionGroup = work_package->execution_group;
- executionGroup->determineChunkRect(&rect, chunkNumber);
+ executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber);
+ executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+ break;
+ }
+ case eWorkPackageType::CustomFunction: {
+ work_package->execute_fn();
+ break;
+ }
+ }
- executionGroup->getOutputOperation()->executeRegion(&rect, chunkNumber);
-
- executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+ if (work_package->executed_fn) {
+ work_package->executed_fn();
+ }
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CPUDevice.h b/source/blender/compositor/intern/COM_CPUDevice.h
index 6df1f41419d..99629890b30 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.h
+++ b/source/blender/compositor/intern/COM_CPUDevice.h
@@ -20,6 +20,8 @@
#include "COM_Device.h"
+namespace blender::compositor {
+
/**
* \brief class representing a CPU device.
* \note for every hardware thread in the system a CPUDevice instance
@@ -43,3 +45,5 @@ class CPUDevice : public Device {
protected:
int m_thread_id;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrder.cc b/source/blender/compositor/intern/COM_ChunkOrder.cc
index 9687154120d..a03718d720d 100644
--- a/source/blender/compositor/intern/COM_ChunkOrder.cc
+++ b/source/blender/compositor/intern/COM_ChunkOrder.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
void ChunkOrder::update_distance(ChunkOrderHotspot *hotspots, unsigned int len_hotspots)
{
double new_distance = DBL_MAX;
@@ -36,3 +38,5 @@ bool operator<(const ChunkOrder &a, const ChunkOrder &b)
{
return a.distance < b.distance;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrder.h b/source/blender/compositor/intern/COM_ChunkOrder.h
index a634309f345..a697f9231d9 100644
--- a/source/blender/compositor/intern/COM_ChunkOrder.h
+++ b/source/blender/compositor/intern/COM_ChunkOrder.h
@@ -24,6 +24,8 @@
#include "COM_ChunkOrderHotspot.h"
+namespace blender::compositor {
+
/** Helper to determine the order how chunks are prioritized during execution. */
struct ChunkOrder {
unsigned int index = 0;
@@ -39,3 +41,5 @@ struct ChunkOrder {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ChunkOrderHotspot")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
index d31ff518ecd..b8e19fc2c34 100644
--- a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
+++ b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
@@ -19,6 +19,8 @@
#include "COM_ChunkOrderHotspot.h"
#include <cmath>
+namespace blender::compositor {
+
double ChunkOrderHotspot::calc_distance(int x, int y)
{
int dx = this->x - x;
@@ -27,3 +29,5 @@ double ChunkOrderHotspot::calc_distance(int x, int y)
result += (double)this->addition;
return result;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrderHotspot.h b/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
index d7f40921836..249b328f5c2 100644
--- a/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
+++ b/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
@@ -22,6 +22,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
struct ChunkOrderHotspot {
int x;
int y;
@@ -37,3 +39,5 @@ struct ChunkOrderHotspot {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ChunkOrderHotspot")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc
index e2447fb5c13..61e299c045e 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.cc
+++ b/source/blender/compositor/intern/COM_CompositorContext.cc
@@ -20,22 +20,43 @@
#include "COM_defines.h"
#include <cstdio>
+#include "BLI_assert.h"
+#include "DNA_userdef_types.h"
+
+namespace blender::compositor {
+
CompositorContext::CompositorContext()
{
this->m_scene = nullptr;
this->m_rd = nullptr;
- this->m_quality = CompositorQuality::High;
+ this->m_quality = eCompositorQuality::High;
this->m_hasActiveOpenCLDevices = false;
this->m_fastCalculation = false;
this->m_viewSettings = nullptr;
this->m_displaySettings = nullptr;
+ this->m_bnodetree = nullptr;
}
int CompositorContext::getFramenumber() const
{
- if (this->m_rd) {
- return this->m_rd->cfra;
- }
+ BLI_assert(m_rd);
+ return m_rd->cfra;
+}
- return -1; /* this should never happen */
+eExecutionModel CompositorContext::get_execution_model() const
+{
+ if (U.experimental.use_full_frame_compositor) {
+ BLI_assert(m_bnodetree != nullptr);
+ switch (m_bnodetree->execution_mode) {
+ case 1:
+ return eExecutionModel::FullFrame;
+ case 0:
+ return eExecutionModel::Tiled;
+ default:
+ BLI_assert(!"Invalid execution mode");
+ }
+ }
+ return eExecutionModel::Tiled;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index 46cf65bbb79..56251511576 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -19,13 +19,18 @@
#pragma once
#include "BLI_rect.h"
-#include "COM_defines.h"
+
+#include "COM_Enums.h"
+
#include "DNA_color_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
+
#include <string>
#include <vector>
+namespace blender::compositor {
+
/**
* \brief Overall context of the compositor
*/
@@ -33,8 +38,8 @@ class CompositorContext {
private:
/**
* \brief The rendering field describes if we are rendering (F12) or if we are editing (Node
- * editor) This field is initialized in ExecutionSystem and must only be read from that point on.
- * \see ExecutionSystem
+ * editor) This field is initialized in ExecutionSystem and must only be read from that point
+ * on. \see ExecutionSystem
*/
bool m_rendering;
@@ -43,7 +48,7 @@ class CompositorContext {
* This field is initialized in ExecutionSystem and must only be read from that point on.
* \see ExecutionSystem
*/
- CompositorQuality m_quality;
+ eCompositorQuality m_quality;
Scene *m_scene;
@@ -200,7 +205,7 @@ class CompositorContext {
/**
* \brief set the quality
*/
- void setQuality(CompositorQuality quality)
+ void setQuality(eCompositorQuality quality)
{
this->m_quality = quality;
}
@@ -208,7 +213,7 @@ class CompositorContext {
/**
* \brief get the quality
*/
- CompositorQuality getQuality() const
+ eCompositorQuality getQuality() const
{
return this->m_quality;
}
@@ -276,4 +281,11 @@ class CompositorContext {
{
return m_rd->size * 0.01f;
}
+
+ /**
+ * Get active execution model.
+ */
+ eExecutionModel get_execution_model() const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index d5bce636b8c..af593b2e1b5 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -26,6 +26,7 @@
#include "COM_NodeOperationBuilder.h"
#include "COM_AlphaOverNode.h"
+#include "COM_AntiAliasingNode.h"
#include "COM_BilateralBlurNode.h"
#include "COM_BlurNode.h"
#include "COM_BokehBlurNode.h"
@@ -115,6 +116,8 @@
#include "COM_ViewerNode.h"
#include "COM_ZCombineNode.h"
+namespace blender::compositor {
+
bool COM_bnode_is_fast_node(const bNode &b_node)
{
return !ELEM(b_node.type,
@@ -418,6 +421,9 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_EXPOSURE:
node = new ExposureNode(b_node);
break;
+ case CMP_NODE_ANTIALIASING:
+ node = new AntiAliasingNode(b_node);
+ break;
}
return node;
}
@@ -454,7 +460,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket)
{
- InputResizeMode mode = toSocket->getResizeMode();
+ ResizeMode mode = toSocket->getResizeMode();
NodeOperation *toOperation = &toSocket->getOperation();
const float toWidth = toOperation->getWidth();
@@ -470,22 +476,22 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
float scaleY = 0;
switch (mode) {
- case COM_SC_NO_RESIZE:
+ case ResizeMode::None:
break;
- case COM_SC_CENTER:
+ case ResizeMode::Center:
doCenter = true;
break;
- case COM_SC_FIT_WIDTH:
+ case ResizeMode::FitWidth:
doCenter = true;
doScale = true;
scaleX = scaleY = toWidth / fromWidth;
break;
- case COM_SC_FIT_HEIGHT:
+ case ResizeMode::FitHeight:
doCenter = true;
doScale = true;
scaleX = scaleY = toHeight / fromHeight;
break;
- case COM_SC_FIT:
+ case ResizeMode::FitAny:
doCenter = true;
doScale = true;
scaleX = toWidth / fromWidth;
@@ -497,7 +503,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
scaleY = scaleX;
}
break;
- case COM_SC_STRETCH:
+ case ResizeMode::Stretch:
doCenter = true;
doScale = true;
scaleX = toWidth / fromWidth;
@@ -510,8 +516,8 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
ScaleOperation *scaleOperation = nullptr;
if (doScale) {
scaleOperation = new ScaleOperation();
- scaleOperation->getInputSocket(1)->setResizeMode(COM_SC_NO_RESIZE);
- scaleOperation->getInputSocket(2)->setResizeMode(COM_SC_NO_RESIZE);
+ scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
first = scaleOperation;
SetValueOperation *sxop = new SetValueOperation();
sxop->setValue(scaleX);
@@ -530,8 +536,8 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
}
TranslateOperation *translateOperation = new TranslateOperation();
- translateOperation->getInputSocket(1)->setResizeMode(COM_SC_NO_RESIZE);
- translateOperation->getInputSocket(2)->setResizeMode(COM_SC_NO_RESIZE);
+ translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
if (!first) {
first = translateOperation;
}
@@ -551,15 +557,17 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
builder.addOperation(translateOperation);
if (doScale) {
- translateOperation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
}
/* remove previous link and replace */
builder.removeInputLink(toSocket);
- first->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
- toSocket->setResizeMode(COM_SC_NO_RESIZE);
+ first->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ toSocket->setResizeMode(ResizeMode::None);
builder.addLink(fromSocket, first->getInputSocket(0));
builder.addLink(translateOperation->getOutputSocket(), toSocket);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h
index 59be34bf0e3..28136437103 100644
--- a/source/blender/compositor/intern/COM_Converter.h
+++ b/source/blender/compositor/intern/COM_Converter.h
@@ -18,14 +18,17 @@
#pragma once
+#include "COM_NodeOperation.h"
+
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
#endif
struct bNode;
+namespace blender::compositor {
+
class Node;
-class NodeOperation;
class NodeOperationInput;
class NodeOperationOutput;
class NodeOperationBuilder;
@@ -65,3 +68,5 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from,
void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index c299bd1c72d..4cf7e09a7d8 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -18,30 +18,29 @@
#include "COM_Debug.h"
-#ifdef COM_DEBUG
-
-# include <map>
-# include <typeinfo>
-# include <vector>
+#include <map>
+#include <typeinfo>
+#include <vector>
extern "C" {
-# include "BLI_fileops.h"
-# include "BLI_path_util.h"
-# include "BLI_string.h"
-# include "BLI_sys_types.h"
-
-# include "BKE_appdir.h"
-# include "BKE_node.h"
-# include "DNA_node_types.h"
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_sys_types.h"
+
+#include "BKE_appdir.h"
+#include "BKE_node.h"
+#include "DNA_node_types.h"
}
-# include "COM_ExecutionGroup.h"
-# include "COM_ExecutionSystem.h"
-# include "COM_Node.h"
+#include "COM_ExecutionSystem.h"
+#include "COM_Node.h"
+
+#include "COM_ReadBufferOperation.h"
+#include "COM_ViewerOperation.h"
+#include "COM_WriteBufferOperation.h"
-# include "COM_ReadBufferOperation.h"
-# include "COM_ViewerOperation.h"
-# include "COM_WriteBufferOperation.h"
+namespace blender::compositor {
int DebugInfo::m_file_index = 0;
DebugInfo::NodeNameMap DebugInfo::m_node_names;
@@ -68,52 +67,8 @@ std::string DebugInfo::operation_name(const NodeOperation *op)
return "";
}
-void DebugInfo::convert_started()
-{
- m_op_names.clear();
-}
-
-void DebugInfo::execute_started(const ExecutionSystem *system)
-{
- m_file_index = 1;
- m_group_states.clear();
- for (ExecutionGroup *execution_group : system->m_groups) {
- m_group_states[execution_group] = EG_WAIT;
- }
-}
-
-void DebugInfo::node_added(const Node *node)
-{
- m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
-}
-
-void DebugInfo::node_to_operations(const Node *node)
-{
- m_current_node_name = m_node_names[node];
-}
-
-void DebugInfo::operation_added(const NodeOperation *operation)
-{
- m_op_names[operation] = m_current_node_name;
-}
-
-void DebugInfo::operation_read_write_buffer(const NodeOperation *operation)
-{
- m_current_op_name = m_op_names[operation];
-}
-
-void DebugInfo::execution_group_started(const ExecutionGroup *group)
-{
- m_group_states[group] = EG_RUNNING;
-}
-
-void DebugInfo::execution_group_finished(const ExecutionGroup *group)
-{
- m_group_states[group] = EG_FINISHED;
-}
-
int DebugInfo::graphviz_operation(const ExecutionSystem *system,
- const NodeOperation *operation,
+ NodeOperation *operation,
const ExecutionGroup *group,
char *str,
int maxlen)
@@ -121,7 +76,7 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
int len = 0;
std::string fillcolor = "gainsboro";
- if (operation->isViewerOperation()) {
+ if (operation->get_flags().is_viewer_operation) {
const ViewerOperation *viewer = (const ViewerOperation *)operation;
if (viewer->isActiveViewerOutput()) {
fillcolor = "lightskyblue1";
@@ -133,13 +88,13 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
else if (operation->isOutputOperation(system->getContext().isRendering())) {
fillcolor = "dodgerblue1";
}
- else if (operation->isSetOperation()) {
+ else if (operation->get_flags().is_set_operation) {
fillcolor = "khaki1";
}
- else if (operation->isReadBufferOperation()) {
+ else if (operation->get_flags().is_read_buffer_operation) {
fillcolor = "darkolivegreen3";
}
- else if (operation->isWriteBufferOperation()) {
+ else if (operation->get_flags().is_write_buffer_operation) {
fillcolor = "darkorange";
}
@@ -256,12 +211,14 @@ int DebugInfo::graphviz_legend_group(
return len;
}
-int DebugInfo::graphviz_legend(char *str, int maxlen)
+int DebugInfo::graphviz_legend(char *str, int maxlen, const bool has_execution_groups)
{
int len = 0;
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n");
- len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n");
+ if (has_execution_groups) {
+ len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n");
+ }
len += snprintf(
str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n");
@@ -281,21 +238,24 @@ int DebugInfo::graphviz_legend(char *str, int maxlen)
"Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0);
len += graphviz_legend_color(
"Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_color(
- "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_color(
- "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0);
+ if (has_execution_groups) {
+ len += graphviz_legend_color(
+ "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_color(
+ "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0);
+ }
len += graphviz_legend_color(
"Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0);
- len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n");
-
- len += graphviz_legend_group(
- "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_group(
- "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_group(
- "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ if (has_execution_groups) {
+ len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n");
+ len += graphviz_legend_group(
+ "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_group(
+ "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_group(
+ "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ }
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "</TABLE>\r\n");
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n");
@@ -360,7 +320,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
for (NodeOperation *operation : system->m_operations) {
- if (operation->isReadBufferOperation()) {
+ if (operation->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read = (ReadBufferOperation *)operation;
WriteBufferOperation *write = read->getMemoryProxy()->getWriteBufferOperation();
std::vector<std::string> &read_groups = op_groups[read];
@@ -381,8 +341,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
for (NodeOperation *op : system->m_operations) {
- for (NodeOperationInput *to : op->m_inputs) {
- NodeOperationOutput *from = to->getLink();
+ for (NodeOperationInput &to : op->m_inputs) {
+ NodeOperationOutput *from = to.getLink();
if (!from) {
continue;
@@ -401,7 +361,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
break;
}
- NodeOperation *to_op = &to->getOperation();
+ NodeOperation *to_op = &to.getOperation();
NodeOperation *from_op = &from->getOperation();
std::vector<std::string> &from_groups = op_groups[from_op];
std::vector<std::string> &to_groups = op_groups[to_op];
@@ -412,7 +372,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
from_op,
from,
to_op,
- to);
+ &to);
for (int k = 0; k < from_groups.size(); k++) {
for (int l = 0; l < to_groups.size(); l++) {
len += snprintf(str + len,
@@ -423,7 +383,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
from,
to_op,
to_groups[l].c_str(),
- to);
+ &to);
len += snprintf(
str + len, maxlen > len ? maxlen - len : 0, " [color=%s]", color.c_str());
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
@@ -432,7 +392,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
}
- len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0);
+ const bool has_execution_groups = system->getContext().get_execution_model() ==
+ eExecutionModel::Tiled;
+ len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups);
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
@@ -441,6 +403,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
void DebugInfo::graphviz(const ExecutionSystem *system)
{
+ if (!COM_EXPORT_GRAPHVIZ) {
+ return;
+ }
char str[1000000];
if (graphviz_system(system, str, sizeof(str) - 1)) {
char basename[FILE_MAX];
@@ -450,48 +415,12 @@ void DebugInfo::graphviz(const ExecutionSystem *system)
BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename);
m_file_index++;
+ std::cout << "Writing compositor debug to: " << filename << "\n";
+
FILE *fp = BLI_fopen(filename, "wb");
fputs(str, fp);
fclose(fp);
}
}
-#else
-
-std::string DebugInfo::node_name(const Node * /*node*/)
-{
- return "";
-}
-std::string DebugInfo::operation_name(const NodeOperation * /*op*/)
-{
- return "";
-}
-void DebugInfo::convert_started()
-{
-}
-void DebugInfo::execute_started(const ExecutionSystem * /*system*/)
-{
-}
-void DebugInfo::node_added(const Node * /*node*/)
-{
-}
-void DebugInfo::node_to_operations(const Node * /*node*/)
-{
-}
-void DebugInfo::operation_added(const NodeOperation * /*operation*/)
-{
-}
-void DebugInfo::operation_read_write_buffer(const NodeOperation * /*operation*/)
-{
-}
-void DebugInfo::execution_group_started(const ExecutionGroup * /*group*/)
-{
-}
-void DebugInfo::execution_group_finished(const ExecutionGroup * /*group*/)
-{
-}
-void DebugInfo::graphviz(const ExecutionSystem * /*system*/)
-{
-}
-
-#endif
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index 0107d8b396d..0de3a5e39dc 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -21,10 +21,14 @@
#include <map>
#include <string>
+#include "COM_ExecutionSystem.h"
+#include "COM_NodeOperation.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
+static constexpr bool COM_EXPORT_GRAPHVIZ = false;
class Node;
-class NodeOperation;
class ExecutionSystem;
class ExecutionGroup;
@@ -39,23 +43,84 @@ class DebugInfo {
static std::string node_name(const Node *node);
static std::string operation_name(const NodeOperation *op);
- static void convert_started();
- static void execute_started(const ExecutionSystem *system);
+ private:
+ static int m_file_index;
+ /** Map nodes to usable names for debug output. */
+ static NodeNameMap m_node_names;
+ /** Map operations to usable names for debug output. */
+ static OpNameMap m_op_names;
+ /** Base name for all operations added by a node. */
+ static std::string m_current_node_name;
+ /** Base name for automatic sub-operations. */
+ static std::string m_current_op_name;
+ /** For visualizing group states. */
+ static GroupStateMap m_group_states;
+
+ public:
+ static void convert_started()
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_op_names.clear();
+ }
+ }
+
+ static void execute_started(const ExecutionSystem *system)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_file_index = 1;
+ m_group_states.clear();
+ for (ExecutionGroup *execution_group : system->m_groups) {
+ m_group_states[execution_group] = EG_WAIT;
+ }
+ }
+ };
+
+ static void node_added(const Node *node)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
+ }
+ }
- static void node_added(const Node *node);
- static void node_to_operations(const Node *node);
- static void operation_added(const NodeOperation *operation);
- static void operation_read_write_buffer(const NodeOperation *operation);
+ static void node_to_operations(const Node *node)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_current_node_name = m_node_names[node];
+ }
+ }
- static void execution_group_started(const ExecutionGroup *group);
- static void execution_group_finished(const ExecutionGroup *group);
+ static void operation_added(const NodeOperation *operation)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_op_names[operation] = m_current_node_name;
+ }
+ };
+
+ static void operation_read_write_buffer(const NodeOperation *operation)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_current_op_name = m_op_names[operation];
+ }
+ };
+
+ static void execution_group_started(const ExecutionGroup *group)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_group_states[group] = EG_RUNNING;
+ }
+ };
+ static void execution_group_finished(const ExecutionGroup *group)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_group_states[group] = EG_FINISHED;
+ }
+ };
static void graphviz(const ExecutionSystem *system);
-#ifdef COM_DEBUG
protected:
static int graphviz_operation(const ExecutionSystem *system,
- const NodeOperation *operation,
+ NodeOperation *operation,
const ExecutionGroup *group,
char *str,
int maxlen);
@@ -64,20 +129,8 @@ class DebugInfo {
const char *name, const char *color, const char *style, char *str, int maxlen);
static int graphviz_legend_group(
const char *name, const char *color, const char *style, char *str, int maxlen);
- static int graphviz_legend(char *str, int maxlen);
+ static int graphviz_legend(char *str, int maxlen, bool has_execution_groups);
static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen);
-
- private:
- static int m_file_index;
- /** Map nodes to usable names for debug output. */
- static NodeNameMap m_node_names;
- /** Map operations to usable names for debug output. */
- static OpNameMap m_op_names;
- /** Base name for all operations added by a node. */
- static std::string m_current_node_name;
- /** Base name for automatic sub-operations. */
- static std::string m_current_op_name;
- /** For visualizing group states. */
- static GroupStateMap m_group_states;
-#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Device.h b/source/blender/compositor/intern/COM_Device.h
index 0a456760045..c848672a405 100644
--- a/source/blender/compositor/intern/COM_Device.h
+++ b/source/blender/compositor/intern/COM_Device.h
@@ -20,6 +20,8 @@
#include "COM_WorkPackage.h"
+namespace blender::compositor {
+
/**
* \brief Abstract class for device implementations to be used by the Compositor.
* devices are queried, initialized and used by the WorkScheduler.
@@ -28,6 +30,14 @@
class Device {
public:
+ Device() = default;
+
+ Device(const Device &other) = delete;
+ Device(Device &&other) noexcept = default;
+
+ Device &operator=(const Device &other) = delete;
+ Device &operator=(Device &&other) = delete;
+
/**
* \brief Declaration of the virtual destructor
* \note resolve warning gcc 4.7
@@ -46,3 +56,5 @@ class Device {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:Device")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Enums.cc b/source/blender/compositor/intern/COM_Enums.cc
new file mode 100644
index 00000000000..d218de92544
--- /dev/null
+++ b/source/blender/compositor/intern/COM_Enums.cc
@@ -0,0 +1,61 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_Enums.h"
+
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority)
+{
+ switch (priority) {
+ case eCompositorPriority::High: {
+ os << "Priority::High";
+ break;
+ }
+ case eCompositorPriority::Medium: {
+ os << "Priority::Medium";
+ break;
+ }
+ case eCompositorPriority::Low: {
+ os << "Priority::Low";
+ break;
+ }
+ }
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state)
+{
+ switch (execution_state) {
+ case eWorkPackageState::NotScheduled: {
+ os << "ExecutionState::NotScheduled";
+ break;
+ }
+ case eWorkPackageState::Scheduled: {
+ os << "ExecutionState::Scheduled";
+ break;
+ }
+ case eWorkPackageState::Executed: {
+ os << "ExecutionState::Executed";
+ break;
+ }
+ }
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h
new file mode 100644
index 00000000000..519e7df940e
--- /dev/null
+++ b/source/blender/compositor/intern/COM_Enums.h
@@ -0,0 +1,91 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_defines.h"
+
+#include <ostream>
+
+namespace blender::compositor {
+
+/**
+ * \brief Possible quality settings
+ * \see CompositorContext.quality
+ * \ingroup Execution
+ */
+enum class eCompositorQuality {
+ /** \brief High quality setting */
+ High = 0,
+ /** \brief Medium quality setting */
+ Medium = 1,
+ /** \brief Low quality setting */
+ Low = 2,
+};
+
+/**
+ * \brief Possible priority settings
+ * \ingroup Execution
+ */
+enum class eCompositorPriority {
+ /** \brief High quality setting */
+ High = 2,
+ /** \brief Medium quality setting */
+ Medium = 1,
+ /** \brief Low quality setting */
+ Low = 0,
+};
+
+/**
+ * \brief the execution state of a chunk in an ExecutionGroup
+ * \ingroup Execution
+ */
+enum class eWorkPackageState {
+ /**
+ * \brief chunk is not yet scheduled
+ */
+ NotScheduled = 0,
+ /**
+ * \brief chunk is scheduled, but not yet executed
+ */
+ Scheduled = 1,
+ /**
+ * \brief chunk is executed.
+ */
+ Executed = 2,
+};
+
+/**
+ * \brief Work type to execute.
+ * \ingroup Execution
+ */
+enum class eWorkPackageType {
+ /**
+ * \brief Executes an execution group tile.
+ */
+ Tile = 0,
+ /**
+ * \brief Executes a custom function.
+ */
+ CustomFunction = 1
+};
+
+std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority);
+std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc
index f500327b7a7..68bda8c70d6 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.cc
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc
@@ -46,10 +46,31 @@
#include "WM_api.h"
#include "WM_types.h"
-ExecutionGroup::ExecutionGroup()
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags)
+{
+ if (flags.initialized) {
+ os << "init,";
+ }
+ if (flags.is_output) {
+ os << "output,";
+ }
+ if (flags.complex) {
+ os << "complex,";
+ }
+ if (flags.open_cl) {
+ os << "open_cl,";
+ }
+ if (flags.single_threaded) {
+ os << "single_threaded,";
+ }
+ return os;
+}
+
+ExecutionGroup::ExecutionGroup(int id)
{
- this->m_is_output = false;
- this->m_complex = false;
+ m_id = id;
this->m_bTree = nullptr;
this->m_height = 0;
this->m_width = 0;
@@ -57,42 +78,48 @@ ExecutionGroup::ExecutionGroup()
this->m_x_chunks_len = 0;
this->m_y_chunks_len = 0;
this->m_chunks_len = 0;
- this->m_initialized = false;
- this->m_openCL = false;
- this->m_singleThreaded = false;
this->m_chunks_finished = 0;
BLI_rcti_init(&this->m_viewerBorder, 0, 0, 0, 0);
this->m_executionStartTime = 0;
}
-CompositorPriority ExecutionGroup::getRenderPriority()
+std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group)
+{
+ os << "ExecutionGroup(id=" << execution_group.get_id();
+ os << ",flags={" << execution_group.get_flags() << "}";
+ os << ",operation=" << *execution_group.getOutputOperation() << "";
+ os << ")";
+ return os;
+}
+
+eCompositorPriority ExecutionGroup::getRenderPriority()
{
return this->getOutputOperation()->getRenderPriority();
}
bool ExecutionGroup::can_contain(NodeOperation &operation)
{
- if (!this->m_initialized) {
+ if (!m_flags.initialized) {
return true;
}
- if (operation.isReadBufferOperation()) {
+ if (operation.get_flags().is_read_buffer_operation) {
return true;
}
- if (operation.isWriteBufferOperation()) {
+ if (operation.get_flags().is_write_buffer_operation) {
return false;
}
- if (operation.isSetOperation()) {
+ if (operation.get_flags().is_set_operation) {
return true;
}
/* complex groups don't allow further ops (except read buffer and values, see above) */
- if (m_complex) {
+ if (m_flags.complex) {
return false;
}
/* complex ops can't be added to other groups (except their own, which they initialize, see
* above) */
- if (operation.isComplex()) {
+ if (operation.get_flags().complex) {
return false;
}
@@ -105,11 +132,12 @@ bool ExecutionGroup::addOperation(NodeOperation *operation)
return false;
}
- if (!operation->isReadBufferOperation() && !operation->isWriteBufferOperation()) {
- m_complex = operation->isComplex();
- m_openCL = operation->isOpenCL();
- m_singleThreaded = operation->isSingleThreaded();
- m_initialized = true;
+ if (!operation->get_flags().is_read_buffer_operation &&
+ !operation->get_flags().is_write_buffer_operation) {
+ m_flags.complex = operation->get_flags().complex;
+ m_flags.open_cl = operation->get_flags().open_cl;
+ m_flags.single_threaded = operation->get_flags().single_threaded;
+ m_flags.initialized = true;
}
m_operations.append(operation);
@@ -123,20 +151,26 @@ NodeOperation *ExecutionGroup::getOutputOperation() const
->m_operations[0]; /* the first operation of the group is always the output operation. */
}
-void ExecutionGroup::initExecution()
+void ExecutionGroup::init_work_packages()
{
- m_chunk_execution_states.clear();
- determineNumberOfChunks();
-
+ m_work_packages.clear();
if (this->m_chunks_len != 0) {
- m_chunk_execution_states.resize(this->m_chunks_len);
- m_chunk_execution_states.fill(eChunkExecutionState::NOT_SCHEDULED);
+ m_work_packages.resize(this->m_chunks_len);
+ for (unsigned int index = 0; index < m_chunks_len; index++) {
+ m_work_packages[index].type = eWorkPackageType::Tile;
+ m_work_packages[index].state = eWorkPackageState::NotScheduled;
+ m_work_packages[index].execution_group = this;
+ m_work_packages[index].chunk_number = index;
+ determineChunkRect(&m_work_packages[index].rect, index);
+ }
}
+}
+void ExecutionGroup::init_read_buffer_operations()
+{
unsigned int max_offset = 0;
-
for (NodeOperation *operation : m_operations) {
- if (operation->isReadBufferOperation()) {
+ if (operation->get_flags().is_read_buffer_operation) {
ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
this->m_read_operations.append(readOperation);
max_offset = MAX2(max_offset, readOperation->getOffset());
@@ -146,15 +180,23 @@ void ExecutionGroup::initExecution()
this->m_max_read_buffer_offset = max_offset;
}
+void ExecutionGroup::initExecution()
+{
+ init_number_of_chunks();
+ init_work_packages();
+ init_read_buffer_operations();
+}
+
void ExecutionGroup::deinitExecution()
{
- m_chunk_execution_states.clear();
+ m_work_packages.clear();
this->m_chunks_len = 0;
this->m_x_chunks_len = 0;
this->m_y_chunks_len = 0;
this->m_read_operations.clear();
this->m_bTree = nullptr;
}
+
void ExecutionGroup::determineResolution(unsigned int resolution[2])
{
NodeOperation *operation = this->getOutputOperation();
@@ -164,9 +206,9 @@ void ExecutionGroup::determineResolution(unsigned int resolution[2])
BLI_rcti_init(&this->m_viewerBorder, 0, this->m_width, 0, this->m_height);
}
-void ExecutionGroup::determineNumberOfChunks()
+void ExecutionGroup::init_number_of_chunks()
{
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
this->m_x_chunks_len = 1;
this->m_y_chunks_len = 1;
this->m_chunks_len = 1;
@@ -181,7 +223,7 @@ void ExecutionGroup::determineNumberOfChunks()
}
}
-blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() const
+blender::Array<unsigned int> ExecutionGroup::get_execution_order() const
{
blender::Array<unsigned int> chunk_order(m_chunks_len);
for (int chunk_index = 0; chunk_index < this->m_chunks_len; chunk_index++) {
@@ -193,7 +235,7 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
float centerY = 0.5f;
ChunkOrdering order_type = ChunkOrdering::Default;
- if (operation->isViewerOperation()) {
+ if (operation->get_flags().is_viewer_operation) {
ViewerOperation *viewer = (ViewerOperation *)operation;
centerX = viewer->getCenterX();
centerY = viewer->getCenterY();
@@ -216,11 +258,10 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
ChunkOrderHotspot hotspot(border_width * centerX, border_height * centerY, 0.0f);
blender::Array<ChunkOrder> chunk_orders(m_chunks_len);
for (index = 0; index < this->m_chunks_len; index++) {
- rcti rect;
- determineChunkRect(&rect, index);
+ const WorkPackage &work_package = m_work_packages[index];
chunk_orders[index].index = index;
- chunk_orders[index].x = rect.xmin - this->m_viewerBorder.xmin;
- chunk_orders[index].y = rect.ymin - this->m_viewerBorder.ymin;
+ chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin;
+ chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin;
chunk_orders[index].update_distance(&hotspot, 1);
}
@@ -254,11 +295,10 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
blender::Array<ChunkOrder> chunk_orders(m_chunks_len);
for (index = 0; index < this->m_chunks_len; index++) {
- rcti rect;
- determineChunkRect(&rect, index);
+ const WorkPackage &work_package = m_work_packages[index];
chunk_orders[index].index = index;
- chunk_orders[index].x = rect.xmin - this->m_viewerBorder.xmin;
- chunk_orders[index].y = rect.ymin - this->m_viewerBorder.ymin;
+ chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin;
+ chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin;
chunk_orders[index].update_distance(hotspots, 9);
}
@@ -301,7 +341,7 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
this->m_chunks_finished = 0;
this->m_bTree = bTree;
- blender::Array<unsigned int> chunk_order = determine_chunk_execution_order();
+ blender::Array<unsigned int> chunk_order = get_execution_order();
DebugInfo::execution_group_started(this);
DebugInfo::graphviz(graph);
@@ -322,8 +362,9 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
chunk_index = chunk_order[index];
int yChunk = chunk_index / this->m_x_chunks_len;
int xChunk = chunk_index - (yChunk * this->m_x_chunks_len);
- switch (m_chunk_execution_states[chunk_index]) {
- case eChunkExecutionState::NOT_SCHEDULED: {
+ const WorkPackage &work_package = m_work_packages[chunk_index];
+ switch (work_package.state) {
+ case eWorkPackageState::NotScheduled: {
scheduleChunkWhenPossible(graph, xChunk, yChunk);
finished = false;
startEvaluated = true;
@@ -334,13 +375,13 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
}
break;
}
- case eChunkExecutionState::SCHEDULED: {
+ case eWorkPackageState::Scheduled: {
finished = false;
startEvaluated = true;
numberEvaluated++;
break;
}
- case eChunkExecutionState::EXECUTED: {
+ case eWorkPackageState::Executed: {
if (!startEvaluated) {
startIndex = index + 1;
}
@@ -360,15 +401,14 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
{
- rcti rect;
- determineChunkRect(&rect, chunkNumber);
+ WorkPackage &work_package = m_work_packages[chunkNumber];
MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN(
sizeof(MemoryBuffer *) * this->m_max_read_buffer_offset, __func__);
rcti output;
for (ReadBufferOperation *readOperation : m_read_operations) {
MemoryProxy *memoryProxy = readOperation->getMemoryProxy();
- this->determineDependingAreaOfInterest(&rect, readOperation, &output);
+ this->determineDependingAreaOfInterest(&work_package.rect, readOperation, &output);
MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(
*memoryProxy, output);
memoryBuffers[readOperation->getOffset()] = memoryBuffer;
@@ -387,8 +427,9 @@ MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy &mem
void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers)
{
- if (this->m_chunk_execution_states[chunkNumber] == eChunkExecutionState::SCHEDULED) {
- this->m_chunk_execution_states[chunkNumber] = eChunkExecutionState::EXECUTED;
+ WorkPackage &work_package = m_work_packages[chunkNumber];
+ if (work_package.state == eWorkPackageState::Scheduled) {
+ work_package.state = eWorkPackageState::Executed;
}
atomic_add_and_fetch_u(&this->m_chunks_finished, 1);
@@ -420,23 +461,23 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo
}
}
-inline void ExecutionGroup::determineChunkRect(rcti *rect,
+inline void ExecutionGroup::determineChunkRect(rcti *r_rect,
const unsigned int xChunk,
const unsigned int yChunk) const
{
const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
BLI_rcti_init(
- rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
+ r_rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
}
else {
const unsigned int minx = xChunk * this->m_chunkSize + this->m_viewerBorder.xmin;
const unsigned int miny = yChunk * this->m_chunkSize + this->m_viewerBorder.ymin;
const unsigned int width = MIN2((unsigned int)this->m_viewerBorder.xmax, this->m_width);
const unsigned int height = MIN2((unsigned int)this->m_viewerBorder.ymax, this->m_height);
- BLI_rcti_init(rect,
+ BLI_rcti_init(r_rect,
MIN2(minx, this->m_width),
MIN2(minx + this->m_chunkSize, width),
MIN2(miny, this->m_height),
@@ -444,18 +485,18 @@ inline void ExecutionGroup::determineChunkRect(rcti *rect,
}
}
-void ExecutionGroup::determineChunkRect(rcti *rect, const unsigned int chunkNumber) const
+void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const
{
const unsigned int yChunk = chunkNumber / this->m_x_chunks_len;
const unsigned int xChunk = chunkNumber - (yChunk * this->m_x_chunks_len);
- determineChunkRect(rect, xChunk, yChunk);
+ determineChunkRect(r_rect, xChunk, yChunk);
}
MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
{
// we assume that this method is only called from complex execution groups.
NodeOperation *operation = this->getOutputOperation();
- if (operation->isWriteBufferOperation()) {
+ if (operation->get_flags().is_write_buffer_operation) {
WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation;
MemoryBuffer *buffer = new MemoryBuffer(
writeOperation->getMemoryProxy(), rect, MemoryBufferState::Temporary);
@@ -466,7 +507,7 @@ MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area)
{
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
return scheduleChunkWhenPossible(graph, 0, 0);
}
// find all chunks inside the rect
@@ -500,9 +541,10 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area
bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
{
- if (this->m_chunk_execution_states[chunkNumber] == eChunkExecutionState::NOT_SCHEDULED) {
- this->m_chunk_execution_states[chunkNumber] = eChunkExecutionState::SCHEDULED;
- WorkScheduler::schedule(this, chunkNumber);
+ WorkPackage &work_package = m_work_packages[chunkNumber];
+ if (work_package.state == eWorkPackageState::NotScheduled) {
+ work_package.state = eWorkPackageState::Scheduled;
+ WorkScheduler::schedule(&work_package);
return true;
}
return false;
@@ -521,22 +563,21 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph,
// Check if chunk is already executed or scheduled and not yet executed.
const int chunk_index = chunk_y * this->m_x_chunks_len + chunk_x;
- if (this->m_chunk_execution_states[chunk_index] == eChunkExecutionState::EXECUTED) {
+ WorkPackage &work_package = m_work_packages[chunk_index];
+ if (work_package.state == eWorkPackageState::Executed) {
return true;
}
- if (this->m_chunk_execution_states[chunk_index] == eChunkExecutionState::SCHEDULED) {
+ if (work_package.state == eWorkPackageState::Scheduled) {
return false;
}
- rcti rect;
- determineChunkRect(&rect, chunk_x, chunk_y);
bool can_be_executed = true;
rcti area;
for (ReadBufferOperation *read_operation : m_read_operations) {
BLI_rcti_init(&area, 0, 0, 0, 0);
MemoryProxy *memory_proxy = read_operation->getMemoryProxy();
- determineDependingAreaOfInterest(&rect, read_operation, &area);
+ determineDependingAreaOfInterest(&work_package.rect, read_operation, &area);
ExecutionGroup *group = memory_proxy->getExecutor();
if (!group->scheduleAreaWhenPossible(graph, &area)) {
@@ -558,16 +599,10 @@ void ExecutionGroup::determineDependingAreaOfInterest(rcti *input,
this->getOutputOperation()->determineDependingAreaOfInterest(input, readOperation, output);
}
-bool ExecutionGroup::isOpenCL()
-{
- return this->m_openCL;
-}
-
void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float ymax)
{
- NodeOperation *operation = this->getOutputOperation();
-
- if (operation->isViewerOperation() || operation->isPreviewOperation()) {
+ const NodeOperation &operation = *this->getOutputOperation();
+ if (operation.get_flags().use_viewer_border) {
BLI_rcti_init(&this->m_viewerBorder,
xmin * this->m_width,
xmax * this->m_width,
@@ -578,32 +613,14 @@ void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float y
void ExecutionGroup::setRenderBorder(float xmin, float xmax, float ymin, float ymax)
{
- NodeOperation *operation = this->getOutputOperation();
-
- if (operation->isOutputOperation(true)) {
- /* Basically, setting border need to happen for only operations
- * which operates in render resolution buffers (like compositor
- * output nodes).
- *
- * In this cases adding border will lead to mapping coordinates
- * from output buffer space to input buffer spaces when executing
- * operation.
- *
- * But nodes like viewer and file output just shall display or
- * safe the same exact buffer which goes to their input, no need
- * in any kind of coordinates mapping.
- */
-
- bool operationNeedsBorder = !(operation->isViewerOperation() ||
- operation->isPreviewOperation() ||
- operation->isFileOutputOperation());
-
- if (operationNeedsBorder) {
- BLI_rcti_init(&this->m_viewerBorder,
- xmin * this->m_width,
- xmax * this->m_width,
- ymin * this->m_height,
- ymax * this->m_height);
- }
+ const NodeOperation &operation = *this->getOutputOperation();
+ if (operation.isOutputOperation(true) && operation.get_flags().use_render_border) {
+ BLI_rcti_init(&this->m_viewerBorder,
+ xmin * this->m_width,
+ xmax * this->m_width,
+ ymin * this->m_height,
+ ymax * this->m_height);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h
index 13ff06cd5d1..cb593feabb0 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.h
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.h
@@ -31,32 +31,50 @@
#include "COM_MemoryProxy.h"
#include "COM_Node.h"
#include "COM_NodeOperation.h"
+#include "COM_WorkPackage.h"
#include <vector>
+namespace blender::compositor {
+
class ExecutionSystem;
class MemoryProxy;
+class MemoryBuffer;
class ReadBufferOperation;
class Device;
-/**
- * \brief the execution state of a chunk in an ExecutionGroup
- * \ingroup Execution
- */
-enum class eChunkExecutionState {
+struct ExecutionGroupFlags {
+ bool initialized : 1;
/**
- * \brief chunk is not yet scheduled
+ * Is this ExecutionGroup an output ExecutionGroup
+ * An OutputExecution group are groups containing a
+ * ViewerOperation, CompositeOperation, PreviewOperation.
*/
- NOT_SCHEDULED = 0,
+ bool is_output : 1;
+ bool complex : 1;
+
/**
- * \brief chunk is scheduled, but not yet executed
+ * Can this ExecutionGroup be scheduled on an OpenCLDevice.
*/
- SCHEDULED = 1,
+ bool open_cl : 1;
+
/**
- * \brief chunk is executed.
+ * Schedule this execution group as a single chunk. This
+ * chunk will be executed by a single thread.
*/
- EXECUTED = 2,
+ bool single_threaded : 1;
+
+ ExecutionGroupFlags()
+ {
+ initialized = false;
+ is_output = false;
+ complex = false;
+ open_cl = false;
+ single_threaded = false;
+ }
};
+std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags);
+
/**
* \brief Class ExecutionGroup is a group of Operations that are executed as one.
* This grouping is used to combine Operations that can be executed as one whole when
@@ -66,18 +84,17 @@ enum class eChunkExecutionState {
class ExecutionGroup {
private:
// fields
-
/**
- * \brief list of operations in this ExecutionGroup
+ * Id of the execution group. For debugging purposes.
*/
- blender::Vector<NodeOperation *> m_operations;
+ int m_id;
/**
- * \brief is this ExecutionGroup an input ExecutionGroup
- * an input execution group is a group that is at the end of the calculation
- * (the output is important for the user).
+ * \brief list of operations in this ExecutionGroup
*/
- bool m_is_output;
+ Vector<NodeOperation *> m_operations;
+
+ ExecutionGroupFlags m_flags;
/**
* \brief Width of the output
@@ -111,21 +128,6 @@ class ExecutionGroup {
unsigned int m_chunks_len;
/**
- * \brief contains this ExecutionGroup a complex NodeOperation.
- */
- bool m_complex;
-
- /**
- * \brief can this ExecutionGroup be scheduled on an OpenCLDevice
- */
- bool m_openCL;
-
- /**
- * \brief Is this Execution group SingleThreaded
- */
- bool m_singleThreaded;
-
- /**
* \brief what is the maximum number field of all ReadBufferOperation in this ExecutionGroup.
* \note this is used to construct the MemoryBuffers that will be passed during execution.
*/
@@ -134,7 +136,7 @@ class ExecutionGroup {
/**
* \brief All read operations of this execution group.
*/
- blender::Vector<ReadBufferOperation *> m_read_operations;
+ Vector<ReadBufferOperation *> m_read_operations;
/**
* \brief reference to the original bNodeTree,
@@ -149,24 +151,9 @@ class ExecutionGroup {
unsigned int m_chunks_finished;
/**
- * \brief m_chunk_execution_states holds per chunk the execution state. this state can be
- * - eChunkExecutionState::NOT_SCHEDULED: not scheduled
- * - eChunkExecutionState::SCHEDULED: scheduled
- * - eChunkExecutionState::EXECUTED: executed
- */
- blender::Vector<eChunkExecutionState> m_chunk_execution_states;
-
- /**
- * \brief indicator when this ExecutionGroup has valid Operations in its vector for Execution
- * \note When building the ExecutionGroup Operations are added via recursion.
- * First a WriteBufferOperations is added, then the.
- * \note Operation containing the settings that is important for the ExecutiongGroup is added,
- * \note When this occurs, these settings are copied over from the node to the ExecutionGroup
- * \note and the Initialized flag is set to true.
- * \see complex
- * \see openCL
+ * \brief m_work_packages holds all unit of work.
*/
- bool m_initialized;
+ Vector<WorkPackage> m_work_packages;
/**
* \brief denotes boundary for border compositing
@@ -187,25 +174,17 @@ class ExecutionGroup {
bool can_contain(NodeOperation &operation);
/**
- * \brief calculate the actual chunk size of this execution group.
- * \note A chunk size is an unsigned int that is both the height and width of a chunk.
- * \note The chunk size will not be stored in the chunkSize field. This needs to be done
- * \note by the calling method.
- */
- unsigned int determineChunkSize();
-
- /**
* \brief Determine the rect (minx, maxx, miny, maxy) of a chunk at a position.
- * \note Only gives useful results after the determination of the chunksize
- * \see determineChunkSize()
*/
- void determineChunkRect(rcti *rect, const unsigned int xChunk, const unsigned int yChunk) const;
+ void determineChunkRect(rcti *r_rect,
+ const unsigned int xChunk,
+ const unsigned int yChunk) const;
/**
* \brief determine the number of chunks, based on the chunkSize, width and height.
* \note The result are stored in the fields numberOfChunks, numberOfXChunks, numberOfYChunks
*/
- void determineNumberOfChunks();
+ void init_number_of_chunks();
/**
* \brief try to schedule a specific chunk.
@@ -252,11 +231,24 @@ class ExecutionGroup {
/**
* Return the execution order of the user visible chunks.
*/
- blender::Array<unsigned int> determine_chunk_execution_order() const;
+ blender::Array<unsigned int> get_execution_order() const;
+
+ void init_read_buffer_operations();
+ void init_work_packages();
public:
// constructors
- ExecutionGroup();
+ ExecutionGroup(int id);
+
+ int get_id() const
+ {
+ return m_id;
+ }
+
+ const ExecutionGroupFlags get_flags() const
+ {
+ return m_flags;
+ }
// methods
/**
@@ -270,23 +262,12 @@ class ExecutionGroup {
bool addOperation(NodeOperation *operation);
/**
- * \brief is this ExecutionGroup an output ExecutionGroup
- * \note An OutputExecution group are groups containing a
- * \note ViewerOperation, CompositeOperation, PreviewOperation.
- * \see NodeOperation.isOutputOperation
- */
- bool isOutputExecutionGroup() const
- {
- return this->m_is_output;
- }
-
- /**
* \brief set whether this ExecutionGroup is an output
* \param isOutput:
*/
void setOutputExecutionGroup(bool is_output)
{
- this->m_is_output = is_output;
+ this->m_flags.is_output = is_output;
}
/**
@@ -322,14 +303,6 @@ class ExecutionGroup {
}
/**
- * \brief does this ExecutionGroup contains a complex NodeOperation
- */
- bool isComplex() const
- {
- return m_complex;
- }
-
- /**
* \brief get the output operation of this ExecutionGroup
* \return NodeOperation *output operation
*/
@@ -404,16 +377,8 @@ class ExecutionGroup {
/**
* \brief Determine the rect (minx, maxx, miny, maxy) of a chunk.
- * \note Only gives useful results after the determination of the chunksize
- * \see determineChunkSize()
- */
- void determineChunkRect(rcti *rect, const unsigned int chunkNumber) const;
-
- /**
- * \brief can this ExecutionGroup be scheduled on an OpenCLDevice
- * \see WorkScheduler.schedule
*/
- bool isOpenCL();
+ void determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const;
void setChunksize(int chunksize)
{
@@ -424,7 +389,7 @@ class ExecutionGroup {
* \brief get the Render priority of this ExecutionGroup
* \see ExecutionSystem.execute
*/
- CompositorPriority getRenderPriority();
+ eCompositorPriority getRenderPriority();
/**
* \brief set border for viewer operation
@@ -441,3 +406,7 @@ class ExecutionGroup {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionGroup")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc
new file mode 100644
index 00000000000..4d7f62e091b
--- /dev/null
+++ b/source/blender/compositor/intern/COM_ExecutionModel.cc
@@ -0,0 +1,48 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_ExecutionModel.h"
+
+namespace blender::compositor {
+
+ExecutionModel::ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations)
+ : context_(context), operations_(operations)
+{
+ const bNodeTree *node_tree = context_.getbNodeTree();
+
+ const rctf *viewer_border = &node_tree->viewer_border;
+ border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) &&
+ viewer_border->xmin < viewer_border->xmax &&
+ viewer_border->ymin < viewer_border->ymax;
+ border_.viewer_border = viewer_border;
+
+ const RenderData *rd = context_.getRenderData();
+ /* Case when cropping to render border happens is handled in
+ * compositor output and render layer nodes. */
+ border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) &&
+ !(rd->mode & R_CROP);
+ border_.render_border = &rd->border;
+}
+
+bool ExecutionModel::is_breaked() const
+{
+ const bNodeTree *btree = context_.getbNodeTree();
+ return btree->test_break(btree->tbh);
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h
new file mode 100644
index 00000000000..9e8466b9282
--- /dev/null
+++ b/source/blender/compositor/intern/COM_ExecutionModel.h
@@ -0,0 +1,84 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "BLI_rect.h"
+#include "BLI_vector.hh"
+
+#include "COM_ExecutionSystem.h"
+
+#include <functional>
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+class NodeOperation;
+
+/**
+ * Base class for execution models. Contains shared implementation.
+ */
+class ExecutionModel {
+ protected:
+ /**
+ * Render and viewer border info. Coordinates are normalized.
+ */
+ struct {
+ bool use_render_border;
+ const rctf *render_border;
+ bool use_viewer_border;
+ const rctf *viewer_border;
+ } border_;
+
+ /**
+ * Context used during execution.
+ */
+ CompositorContext &context_;
+
+ /**
+ * All operations being executed.
+ */
+ Span<NodeOperation *> operations_;
+
+ public:
+ ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations);
+
+ virtual ~ExecutionModel()
+ {
+ }
+
+ virtual void execute(ExecutionSystem &exec_system) = 0;
+
+ virtual void execute_work(const rcti &UNUSED(work_rect),
+ std::function<void(const rcti &split_rect)> UNUSED(work_func))
+ {
+ BLI_assert(!"Method not supported by current execution model");
+ }
+
+ protected:
+ bool is_breaked() const;
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc
index df97b8079b2..a12ec774032 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.cc
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc
@@ -21,22 +21,19 @@
#include "BLI_utildefines.h"
#include "PIL_time.h"
-#include "BKE_node.h"
-
-#include "BLT_translation.h"
-
-#include "COM_Converter.h"
#include "COM_Debug.h"
-#include "COM_ExecutionGroup.h"
+#include "COM_FullFrameExecutionModel.h"
#include "COM_NodeOperation.h"
#include "COM_NodeOperationBuilder.h"
-#include "COM_ReadBufferOperation.h"
+#include "COM_TiledExecutionModel.h"
#include "COM_WorkScheduler.h"
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
ExecutionSystem::ExecutionSystem(RenderData *rd,
Scene *scene,
bNodeTree *editingtree,
@@ -53,10 +50,10 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
this->m_context.setFastCalculation(fastcalculation);
/* initialize the CompositorContext */
if (rendering) {
- this->m_context.setQuality((CompositorQuality)editingtree->render_quality);
+ this->m_context.setQuality((eCompositorQuality)editingtree->render_quality);
}
else {
- this->m_context.setQuality((CompositorQuality)editingtree->edit_quality);
+ this->m_context.setQuality((eCompositorQuality)editingtree->edit_quality);
}
this->m_context.setRendering(rendering);
this->m_context.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() &&
@@ -71,41 +68,23 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
builder.convertToOperations(this);
}
- unsigned int resolution[2];
-
- rctf *viewer_border = &editingtree->viewer_border;
- bool use_viewer_border = (editingtree->flag & NTREE_VIEWER_BORDER) &&
- viewer_border->xmin < viewer_border->xmax &&
- viewer_border->ymin < viewer_border->ymax;
-
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Determining resolution"));
-
- for (ExecutionGroup *executionGroup : m_groups) {
- resolution[0] = 0;
- resolution[1] = 0;
- executionGroup->determineResolution(resolution);
-
- if (rendering) {
- /* case when cropping to render border happens is handled in
- * compositor output and render layer nodes
- */
- if ((rd->mode & R_BORDER) && !(rd->mode & R_CROP)) {
- executionGroup->setRenderBorder(
- rd->border.xmin, rd->border.xmax, rd->border.ymin, rd->border.ymax);
- }
- }
-
- if (use_viewer_border) {
- executionGroup->setViewerBorder(
- viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax);
- }
+ switch (m_context.get_execution_model()) {
+ case eExecutionModel::Tiled:
+ execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups);
+ break;
+ case eExecutionModel::FullFrame:
+ execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations);
+ break;
+ default:
+ BLI_assert(!"Non implemented execution model");
+ break;
}
-
- // DebugInfo::graphviz(this);
}
ExecutionSystem::~ExecutionSystem()
{
+ delete execution_model_;
+
for (NodeOperation *operation : m_operations) {
delete operation;
}
@@ -117,105 +96,23 @@ ExecutionSystem::~ExecutionSystem()
this->m_groups.clear();
}
-void ExecutionSystem::set_operations(const blender::Vector<NodeOperation *> &operations,
- const blender::Vector<ExecutionGroup *> &groups)
+void ExecutionSystem::set_operations(const Vector<NodeOperation *> &operations,
+ const Vector<ExecutionGroup *> &groups)
{
m_operations = operations;
m_groups = groups;
}
-static void update_read_buffer_offset(blender::Vector<NodeOperation *> &operations)
-{
- unsigned int order = 0;
- for (NodeOperation *operation : operations) {
- if (operation->isReadBufferOperation()) {
- ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
- readOperation->setOffset(order);
- order++;
- }
- }
-}
-
-static void init_write_operations_for_execution(blender::Vector<NodeOperation *> &operations,
- const bNodeTree *bTree)
-{
- for (NodeOperation *operation : operations) {
- if (operation->isWriteBufferOperation()) {
- operation->setbNodeTree(bTree);
- operation->initExecution();
- }
- }
-}
-
-static void link_write_buffers(blender::Vector<NodeOperation *> &operations)
-{
- for (NodeOperation *operation : operations) {
- if (operation->isReadBufferOperation()) {
- ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
- readOperation->updateMemoryBuffer();
- }
- }
-}
-
-static void init_non_write_operations_for_execution(blender::Vector<NodeOperation *> &operations,
- const bNodeTree *bTree)
-{
- for (NodeOperation *operation : operations) {
- if (!operation->isWriteBufferOperation()) {
- operation->setbNodeTree(bTree);
- operation->initExecution();
- }
- }
-}
-
-static void init_execution_groups_for_execution(blender::Vector<ExecutionGroup *> &groups,
- const int chunk_size)
-{
- for (ExecutionGroup *execution_group : groups) {
- execution_group->setChunksize(chunk_size);
- execution_group->initExecution();
- }
-}
-
void ExecutionSystem::execute()
{
- const bNodeTree *editingtree = this->m_context.getbNodeTree();
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution"));
-
DebugInfo::execute_started(this);
- update_read_buffer_offset(m_operations);
-
- init_write_operations_for_execution(m_operations, m_context.getbNodeTree());
- link_write_buffers(m_operations);
- init_non_write_operations_for_execution(m_operations, m_context.getbNodeTree());
- init_execution_groups_for_execution(m_groups, m_context.getChunksize());
-
- WorkScheduler::start(this->m_context);
- execute_groups(CompositorPriority::High);
- if (!this->getContext().isFastCalculation()) {
- execute_groups(CompositorPriority::Medium);
- execute_groups(CompositorPriority::Low);
- }
- WorkScheduler::finish();
- WorkScheduler::stop();
-
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution"));
-
- for (NodeOperation *operation : m_operations) {
- operation->deinitExecution();
- }
-
- for (ExecutionGroup *execution_group : m_groups) {
- execution_group->deinitExecution();
- }
+ execution_model_->execute(*this);
}
-void ExecutionSystem::execute_groups(CompositorPriority priority)
+void ExecutionSystem::execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func)
{
- for (ExecutionGroup *execution_group : m_groups) {
- if (execution_group->isOutputExecutionGroup() &&
- execution_group->getRenderPriority() == priority) {
- execution_group->execute(this);
- }
- }
+ execution_model_->execute_work(work_rect, work_func);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h
index c12380fe839..e106209651c 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.h
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.h
@@ -25,12 +25,15 @@ class ExecutionGroup;
#include "COM_ExecutionGroup.h"
#include "COM_Node.h"
#include "COM_NodeOperation.h"
+#include "COM_SharedOperationBuffers.h"
#include "DNA_color_types.h"
#include "DNA_node_types.h"
#include "BLI_vector.hh"
+namespace blender::compositor {
+
/**
* \page execution Execution model
* In order to get to an efficient model for execution, several steps are being done. these steps
@@ -70,17 +73,17 @@ class ExecutionGroup;
*
* - Image size conversions: the system can automatically convert when resolutions do not match.
* An NodeInput has a resize mode. This can be any of the following settings.
- * - [@ref InputSocketResizeMode.COM_SC_CENTER]:
+ * - [@ref InputSocketResizeMode.ResizeMode::Center]:
* The center of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT_WIDTH]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitWidth]:
* The width of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT_HEIGHT]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitHeight]:
* The height of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitAny]:
* The width, or the height of both images are aligned to make sure that it fits.
- * - [@ref InputSocketResizeMode.COM_SC_STRETCH]:
+ * - [@ref InputSocketResizeMode.ResizeMode::Stretch]:
* The width and the height of both images are aligned.
- * - [@ref InputSocketResizeMode.COM_SC_NO_RESIZE]:
+ * - [@ref InputSocketResizeMode.ResizeMode::None]:
* Bottom left of the images are aligned.
*
* \see COM_convert_data_type Datatype conversions
@@ -113,13 +116,21 @@ class ExecutionGroup;
* \see ExecutionGroup class representing the ExecutionGroup
*/
+/* Forward declarations. */
+class ExecutionModel;
+
/**
* \brief the ExecutionSystem contains the whole compositor tree.
*/
class ExecutionSystem {
-
private:
/**
+ * Contains operations active buffers data. Buffers will be disposed once reader operations are
+ * finished.
+ */
+ SharedOperationBuffers active_buffers_;
+
+ /**
* \brief the context used during execution
*/
CompositorContext m_context;
@@ -127,12 +138,17 @@ class ExecutionSystem {
/**
* \brief vector of operations
*/
- blender::Vector<NodeOperation *> m_operations;
+ Vector<NodeOperation *> m_operations;
/**
* \brief vector of groups
*/
- blender::Vector<ExecutionGroup *> m_groups;
+ Vector<ExecutionGroup *> m_groups;
+
+ /**
+ * Active execution model implementation.
+ */
+ ExecutionModel *execution_model_;
private: // methods
public:
@@ -157,8 +173,8 @@ class ExecutionSystem {
*/
~ExecutionSystem();
- void set_operations(const blender::Vector<NodeOperation *> &operations,
- const blender::Vector<ExecutionGroup *> &groups);
+ void set_operations(const Vector<NodeOperation *> &operations,
+ const Vector<ExecutionGroup *> &groups);
/**
* \brief execute this system
@@ -176,9 +192,14 @@ class ExecutionSystem {
return this->m_context;
}
- private:
- void execute_groups(CompositorPriority priority);
+ SharedOperationBuffers &get_active_buffers()
+ {
+ return active_buffers_;
+ }
+ void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func);
+
+ private:
/* allow the DebugInfo class to look at internals */
friend class DebugInfo;
@@ -186,3 +207,5 @@ class ExecutionSystem {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionSystem")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
new file mode 100644
index 00000000000..21075bb7255
--- /dev/null
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -0,0 +1,362 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_FullFrameExecutionModel.h"
+#include "COM_Debug.h"
+#include "COM_ExecutionGroup.h"
+#include "COM_ReadBufferOperation.h"
+#include "COM_WorkScheduler.h"
+
+#include "BLT_translation.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context,
+ SharedOperationBuffers &shared_buffers,
+ Span<NodeOperation *> operations)
+ : ExecutionModel(context, operations),
+ active_buffers_(shared_buffers),
+ num_operations_finished_(0),
+ work_mutex_(),
+ work_finished_cond_()
+{
+ priorities_.append(eCompositorPriority::High);
+ if (!context.isFastCalculation()) {
+ priorities_.append(eCompositorPriority::Medium);
+ priorities_.append(eCompositorPriority::Low);
+ }
+
+ BLI_mutex_init(&work_mutex_);
+ BLI_condition_init(&work_finished_cond_);
+}
+
+FullFrameExecutionModel::~FullFrameExecutionModel()
+{
+ BLI_condition_end(&work_finished_cond_);
+ BLI_mutex_end(&work_mutex_);
+}
+
+void FullFrameExecutionModel::execute(ExecutionSystem &exec_system)
+{
+ const bNodeTree *node_tree = this->context_.getbNodeTree();
+ node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution"));
+
+ DebugInfo::graphviz(&exec_system);
+
+ determine_areas_to_render_and_reads();
+ render_operations(exec_system);
+}
+
+void FullFrameExecutionModel::determine_areas_to_render_and_reads()
+{
+ const bool is_rendering = context_.isRendering();
+ const bNodeTree *node_tree = context_.getbNodeTree();
+
+ rcti area;
+ for (eCompositorPriority priority : priorities_) {
+ for (NodeOperation *op : operations_) {
+ op->setbNodeTree(node_tree);
+ if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) {
+ get_output_render_area(op, area);
+ determine_areas_to_render(op, area);
+ determine_reads(op);
+ }
+ }
+ }
+}
+
+Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op)
+{
+ const int num_inputs = op->getNumberOfInputSockets();
+ Vector<MemoryBuffer *> inputs_buffers(num_inputs);
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = op->get_input_operation(i);
+ inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op);
+ }
+ return inputs_buffers;
+}
+
+MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op)
+{
+ rcti op_rect;
+ BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight());
+
+ const DataType data_type = op->getOutputSocket(0)->getDataType();
+ /* TODO: We should check if the operation is constant instead of is_set_operation. Finding a way
+ * to know if an operation is constant has to be implemented yet. */
+ const bool is_a_single_elem = op->get_flags().is_set_operation;
+ return new MemoryBuffer(data_type, op_rect, is_a_single_elem);
+}
+
+void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSystem &exec_system)
+{
+ Vector<MemoryBuffer *> input_bufs = get_input_buffers(op);
+
+ const bool has_outputs = op->getNumberOfOutputSockets() > 0;
+ MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr;
+ Span<rcti> areas = active_buffers_.get_areas_to_render(op);
+ op->render(op_buf, areas, input_bufs, exec_system);
+ active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf));
+
+ operation_finished(op);
+}
+
+/**
+ * Render output operations in order of priority.
+ */
+void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system)
+{
+ const bool is_rendering = context_.isRendering();
+
+ WorkScheduler::start(this->context_);
+ for (eCompositorPriority priority : priorities_) {
+ for (NodeOperation *op : operations_) {
+ if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) {
+ render_output_dependencies(op, exec_system);
+ render_operation(op, exec_system);
+ }
+ }
+ }
+ WorkScheduler::stop();
+}
+
+/**
+ * Returns all dependencies from inputs to outputs. A dependency may be repeated when
+ * several operations depend on it.
+ */
+static Vector<NodeOperation *> get_operation_dependencies(NodeOperation *operation)
+{
+ /* Get dependencies from outputs to inputs. */
+ Vector<NodeOperation *> dependencies;
+ Vector<NodeOperation *> next_outputs;
+ next_outputs.append(operation);
+ while (next_outputs.size() > 0) {
+ Vector<NodeOperation *> outputs(next_outputs);
+ next_outputs.clear();
+ for (NodeOperation *output : outputs) {
+ for (int i = 0; i < output->getNumberOfInputSockets(); i++) {
+ next_outputs.append(output->get_input_operation(i));
+ }
+ }
+ dependencies.extend(next_outputs);
+ }
+
+ /* Reverse to get dependencies from inputs to outputs. */
+ std::reverse(dependencies.begin(), dependencies.end());
+
+ return dependencies;
+}
+
+void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_op,
+ ExecutionSystem &exec_system)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+ Vector<NodeOperation *> dependencies = get_operation_dependencies(output_op);
+ for (NodeOperation *op : dependencies) {
+ if (!active_buffers_.is_operation_rendered(op)) {
+ render_operation(op, exec_system);
+ }
+ }
+}
+
+/**
+ * Determines all operations areas needed to render given output area.
+ */
+void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op,
+ const rcti &output_area)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ Vector<std::pair<NodeOperation *, const rcti>> stack;
+ stack.append({output_op, output_area});
+ while (stack.size() > 0) {
+ std::pair<NodeOperation *, rcti> pair = stack.pop_last();
+ NodeOperation *operation = pair.first;
+ const rcti &render_area = pair.second;
+ if (active_buffers_.is_area_registered(operation, render_area)) {
+ continue;
+ }
+
+ active_buffers_.register_area(operation, render_area);
+
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ rcti input_op_rect, input_area;
+ BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight());
+ operation->get_area_of_interest(input_op, render_area, input_area);
+
+ /* Ensure area of interest is within operation bounds, cropping areas outside. */
+ BLI_rcti_isect(&input_area, &input_op_rect, &input_area);
+
+ stack.append({input_op, input_area});
+ }
+ }
+}
+
+/**
+ * Determines reads to receive by operations in output operation tree (i.e: Number of dependent
+ * operations each operation has).
+ */
+void FullFrameExecutionModel::determine_reads(NodeOperation *output_op)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ Vector<NodeOperation *> stack;
+ stack.append(output_op);
+ while (stack.size() > 0) {
+ NodeOperation *operation = stack.pop_last();
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ if (!active_buffers_.has_registered_reads(input_op)) {
+ stack.append(input_op);
+ }
+ active_buffers_.register_read(input_op);
+ }
+ }
+}
+
+/**
+ * Calculates given output operation area to be rendered taking into account viewer and render
+ * borders.
+ */
+void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ /* By default return operation bounds (no border). */
+ const int op_width = output_op->getWidth();
+ const int op_height = output_op->getHeight();
+ BLI_rcti_init(&r_area, 0, op_width, 0, op_height);
+
+ const bool has_viewer_border = border_.use_viewer_border &&
+ (output_op->get_flags().is_viewer_operation ||
+ output_op->get_flags().is_preview_operation);
+ const bool has_render_border = border_.use_render_border;
+ if (has_viewer_border || has_render_border) {
+ /* Get border with normalized coordinates. */
+ const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border;
+
+ /* Return de-normalized border. */
+ BLI_rcti_init(&r_area,
+ norm_border->xmin * op_width,
+ norm_border->xmax * op_width,
+ norm_border->ymin * op_height,
+ norm_border->ymax * op_height);
+ }
+}
+
+/**
+ * Multi-threadedly execute given work function passing work_rect splits as argument.
+ */
+void FullFrameExecutionModel::execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func)
+{
+ if (is_breaked()) {
+ return;
+ }
+
+ /* Split work vertically to maximize continuous memory. */
+ const int work_height = BLI_rcti_size_y(&work_rect);
+ const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height);
+ const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works;
+ int remaining_height = work_height - split_height * num_sub_works;
+
+ Vector<WorkPackage> sub_works(num_sub_works);
+ int sub_work_y = work_rect.ymin;
+ int num_sub_works_finished = 0;
+ for (int i = 0; i < num_sub_works; i++) {
+ int sub_work_height = split_height;
+
+ /* Distribute remaining height between sub-works. */
+ if (remaining_height > 0) {
+ sub_work_height++;
+ remaining_height--;
+ }
+
+ WorkPackage &sub_work = sub_works[i];
+ sub_work.type = eWorkPackageType::CustomFunction;
+ sub_work.execute_fn = [=, &work_func, &work_rect]() {
+ if (is_breaked()) {
+ return;
+ }
+ rcti split_rect;
+ BLI_rcti_init(
+ &split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height);
+ work_func(split_rect);
+ };
+ sub_work.executed_fn = [&]() {
+ BLI_mutex_lock(&work_mutex_);
+ num_sub_works_finished++;
+ if (num_sub_works_finished == num_sub_works) {
+ BLI_condition_notify_one(&work_finished_cond_);
+ }
+ BLI_mutex_unlock(&work_mutex_);
+ };
+ WorkScheduler::schedule(&sub_work);
+ sub_work_y += sub_work_height;
+ }
+ BLI_assert(sub_work_y == work_rect.ymax);
+
+ WorkScheduler::finish();
+
+ /* Ensure all sub-works finished.
+ * TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading
+ * model. Sync code should be removed once it's fixed. */
+ BLI_mutex_lock(&work_mutex_);
+ if (num_sub_works_finished < num_sub_works) {
+ BLI_condition_wait(&work_finished_cond_, &work_mutex_);
+ }
+ BLI_mutex_unlock(&work_mutex_);
+}
+
+void FullFrameExecutionModel::operation_finished(NodeOperation *operation)
+{
+ /* Report inputs reads so that buffers may be freed/reused. */
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ active_buffers_.read_finished(operation->get_input_operation(i));
+ }
+
+ num_operations_finished_++;
+ update_progress_bar();
+}
+
+void FullFrameExecutionModel::update_progress_bar()
+{
+ const bNodeTree *tree = context_.getbNodeTree();
+ if (tree) {
+ const float progress = num_operations_finished_ / static_cast<float>(operations_.size());
+ tree->progress(tree->prh, progress);
+
+ char buf[128];
+ BLI_snprintf(buf,
+ sizeof(buf),
+ TIP_("Compositing | Operation %i-%li"),
+ num_operations_finished_ + 1,
+ operations_.size());
+ tree->stats_draw(tree->sdh, buf);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
new file mode 100644
index 00000000000..e68ad93b407
--- /dev/null
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
@@ -0,0 +1,88 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_ExecutionModel.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+class ExecutionGroup;
+
+/**
+ * Fully renders operations in order from inputs to outputs.
+ */
+class FullFrameExecutionModel : public ExecutionModel {
+ private:
+ /**
+ * Contains operations active buffers data. Buffers will be disposed once reader operations are
+ * finished.
+ */
+ SharedOperationBuffers &active_buffers_;
+
+ /**
+ * Number of operations finished.
+ */
+ int num_operations_finished_;
+
+ /**
+ * Order of priorities for output operations execution.
+ */
+ Vector<eCompositorPriority> priorities_;
+
+ ThreadMutex work_mutex_;
+ ThreadCondition work_finished_cond_;
+
+ public:
+ FullFrameExecutionModel(CompositorContext &context,
+ SharedOperationBuffers &shared_buffers,
+ Span<NodeOperation *> operations);
+ ~FullFrameExecutionModel();
+
+ void execute(ExecutionSystem &exec_system) override;
+
+ void execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func) override;
+
+ private:
+ void determine_areas_to_render_and_reads();
+ void render_operations(ExecutionSystem &exec_system);
+ void render_output_dependencies(NodeOperation *output_op, ExecutionSystem &exec_system);
+ Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op);
+ MemoryBuffer *create_operation_buffer(NodeOperation *op);
+ void render_operation(NodeOperation *op, ExecutionSystem &exec_system);
+
+ void operation_finished(NodeOperation *operation);
+
+ void get_output_render_area(NodeOperation *output_op, rcti &r_area);
+ void determine_areas_to_render(NodeOperation *output_op, const rcti &output_area);
+ void determine_reads(NodeOperation *output_op);
+
+ void update_progress_bar();
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:FullFrameExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index 0b28168720e..8c30d3215d7 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -20,45 +20,54 @@
#include "MEM_guardedalloc.h"
-static unsigned int determine_num_channels(DataType datatype)
-{
- switch (datatype) {
- case DataType::Value:
- return COM_NUM_CHANNELS_VALUE;
- case DataType::Vector:
- return COM_NUM_CHANNELS_VECTOR;
- case DataType::Color:
- default:
- return COM_NUM_CHANNELS_COLOR;
- }
-}
+namespace blender::compositor {
MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state)
{
m_rect = rect;
+ this->m_is_a_single_elem = false;
this->m_memoryProxy = memoryProxy;
- this->m_num_channels = determine_num_channels(memoryProxy->getDataType());
+ this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType());
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = state;
this->m_datatype = memoryProxy->getDataType();
+
+ set_strides();
}
-MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect)
+MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem)
{
m_rect = rect;
+ this->m_is_a_single_elem = is_a_single_elem;
this->m_memoryProxy = nullptr;
- this->m_num_channels = determine_num_channels(dataType);
+ this->m_num_channels = COM_data_type_num_channels(dataType);
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = MemoryBufferState::Temporary;
this->m_datatype = dataType;
+
+ set_strides();
}
MemoryBuffer::MemoryBuffer(const MemoryBuffer &src)
- : MemoryBuffer(src.m_memoryProxy, src.m_rect, MemoryBufferState::Temporary)
+ : MemoryBuffer(src.m_datatype, src.m_rect, false)
+{
+ m_memoryProxy = src.m_memoryProxy;
+ /* src may be single elem buffer */
+ fill_from(src);
+}
+
+void MemoryBuffer::set_strides()
{
- memcpy(m_buffer, src.m_buffer, buffer_len() * m_num_channels * sizeof(float));
+ if (m_is_a_single_elem) {
+ this->elem_stride = 0;
+ this->row_stride = 0;
+ }
+ else {
+ this->elem_stride = m_num_channels;
+ this->row_stride = getWidth() * m_num_channels;
+ }
}
void MemoryBuffer::clear()
@@ -111,6 +120,8 @@ MemoryBuffer::~MemoryBuffer()
void MemoryBuffer::fill_from(const MemoryBuffer &src)
{
+ BLI_assert(!this->is_a_single_elem());
+
unsigned int otherY;
unsigned int minX = MAX2(this->m_rect.xmin, src.m_rect.xmin);
unsigned int maxX = MIN2(this->m_rect.xmax, src.m_rect.xmax);
@@ -120,10 +131,8 @@ void MemoryBuffer::fill_from(const MemoryBuffer &src)
int otherOffset;
for (otherY = minY; otherY < maxY; otherY++) {
- otherOffset = ((otherY - src.m_rect.ymin) * src.getWidth() + minX - src.m_rect.xmin) *
- this->m_num_channels;
- offset = ((otherY - this->m_rect.ymin) * getWidth() + minX - this->m_rect.xmin) *
- this->m_num_channels;
+ otherOffset = src.get_coords_offset(minX, otherY);
+ offset = this->get_coords_offset(minX, otherY);
memcpy(&this->m_buffer[offset],
&src.m_buffer[otherOffset],
(maxX - minX) * this->m_num_channels * sizeof(float));
@@ -134,8 +143,7 @@ void MemoryBuffer::writePixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
- const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
- this->m_num_channels;
+ const int offset = get_coords_offset(x, y);
memcpy(&this->m_buffer[offset], color, sizeof(float) * this->m_num_channels);
}
}
@@ -144,8 +152,7 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
- const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
- this->m_num_channels;
+ const int offset = get_coords_offset(x, y);
float *dst = &this->m_buffer[offset];
const float *src = color;
for (int i = 0; i < this->m_num_channels; i++, dst++, src++) {
@@ -162,24 +169,31 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4]
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
{
- BLI_assert(this->m_datatype == DataType::Color);
- float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
- /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
- * but compositor uses pixel space. For now let's just divide the values and
- * switch compositor to normalized space for EWA later.
- */
- float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height};
- float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height};
- float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height};
-
- BLI_ewa_filter(this->getWidth(),
- this->getHeight(),
- false,
- true,
- uv_normal,
- du_normal,
- dv_normal,
- read_ewa_pixel_sampled,
- this,
- result);
+ if (m_is_a_single_elem) {
+ memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
+ }
+ else {
+ BLI_assert(this->m_datatype == DataType::Color);
+ float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
+ /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
+ * but compositor uses pixel space. For now let's just divide the values and
+ * switch compositor to normalized space for EWA later.
+ */
+ float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height};
+ float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height};
+ float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height};
+
+ BLI_ewa_filter(this->getWidth(),
+ this->getHeight(),
+ false,
+ true,
+ uv_normal,
+ du_normal,
+ dv_normal,
+ read_ewa_pixel_sampled,
+ this,
+ result);
+ }
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index 6f719b61122..97b220508e0 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -16,17 +16,16 @@
* Copyright 2011, Blender Foundation.
*/
-class MemoryBuffer;
-
#pragma once
#include "COM_ExecutionGroup.h"
#include "COM_MemoryProxy.h"
-#include "COM_SocketReader.h"
#include "BLI_math.h"
#include "BLI_rect.h"
+namespace blender::compositor {
+
/**
* \brief state of a memory buffer
* \ingroup Memory
@@ -51,6 +50,25 @@ class MemoryProxy;
* \brief a MemoryBuffer contains access to the data of a chunk
*/
class MemoryBuffer {
+ public:
+ /**
+ * Offset between elements.
+ *
+ * Should always be used for the x dimension when calculating buffer offsets.
+ * It will be 0 when is_a_single_elem=true.
+ * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
+ */
+ int elem_stride;
+
+ /**
+ * Offset between rows.
+ *
+ * Should always be used for the y dimension when calculating buffer offsets.
+ * It will be 0 when is_a_single_elem=true.
+ * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
+ */
+ int row_stride;
+
private:
/**
* \brief proxy of the memory (same for all chunks in the same buffer)
@@ -83,6 +101,11 @@ class MemoryBuffer {
*/
uint8_t m_num_channels;
+ /**
+ * Whether buffer is a single element in memory.
+ */
+ bool m_is_a_single_elem;
+
public:
/**
* \brief construct new temporarily MemoryBuffer for an area
@@ -92,7 +115,7 @@ class MemoryBuffer {
/**
* \brief construct new temporarily MemoryBuffer for an area
*/
- MemoryBuffer(DataType datatype, const rcti &rect);
+ MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false);
/**
* Copy constructor
@@ -104,6 +127,102 @@ class MemoryBuffer {
*/
~MemoryBuffer();
+ /**
+ * Whether buffer is a single element in memory independently of its resolution. True for set
+ * operations buffers.
+ */
+ bool is_a_single_elem() const
+ {
+ return m_is_a_single_elem;
+ }
+
+ float &operator[](int index)
+ {
+ BLI_assert(m_is_a_single_elem ? index < m_num_channels :
+ index < get_coords_offset(getWidth(), getHeight()));
+ return m_buffer[index];
+ }
+
+ const float &operator[](int index) const
+ {
+ BLI_assert(m_is_a_single_elem ? index < m_num_channels :
+ index < get_coords_offset(getWidth(), getHeight()));
+ return m_buffer[index];
+ }
+
+ /**
+ * Get offset needed to jump from buffer start to given coordinates.
+ */
+ int get_coords_offset(int x, int y) const
+ {
+ return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride;
+ }
+
+ /**
+ * Get buffer element at given coordinates.
+ */
+ float *get_elem(int x, int y)
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ return m_buffer + get_coords_offset(x, y);
+ }
+
+ /**
+ * Get buffer element at given coordinates.
+ */
+ const float *get_elem(int x, int y) const
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ return m_buffer + get_coords_offset(x, y);
+ }
+
+ /**
+ * Get channel value at given coordinates.
+ */
+ float &get_value(int x, int y, int channel)
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
+ channel >= 0 && channel < m_num_channels);
+ return m_buffer[get_coords_offset(x, y) + channel];
+ }
+
+ /**
+ * Get channel value at given coordinates.
+ */
+ const float &get_value(int x, int y, int channel) const
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
+ channel >= 0 && channel < m_num_channels);
+ return m_buffer[get_coords_offset(x, y) + channel];
+ }
+
+ /**
+ * Get the buffer row end.
+ */
+ const float *get_row_end(int y) const
+ {
+ BLI_assert(y >= 0 && y < getHeight());
+ return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
+ }
+
+ /**
+ * Get the number of elements in memory for a row. For single element buffers it will always
+ * be 1.
+ */
+ int get_memory_width() const
+ {
+ return is_a_single_elem() ? 1 : getWidth();
+ }
+
+ /**
+ * Get number of elements in memory for a column. For single element buffers it will
+ * always be 1.
+ */
+ int get_memory_height() const
+ {
+ return is_a_single_elem() ? 1 : getHeight();
+ }
+
uint8_t get_num_channels()
{
return this->m_num_channels;
@@ -217,7 +336,7 @@ class MemoryBuffer {
int u = x;
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
- const int offset = (getWidth() * y + x) * this->m_num_channels;
+ const int offset = get_coords_offset(u, v);
float *buffer = &this->m_buffer[offset];
memcpy(result, buffer, sizeof(float) * this->m_num_channels);
}
@@ -233,7 +352,7 @@ class MemoryBuffer {
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
- const int offset = (getWidth() * v + u) * this->m_num_channels;
+ const int offset = get_coords_offset(u, v);
BLI_assert(offset >= 0);
BLI_assert(offset < this->buffer_len() * this->m_num_channels);
@@ -259,15 +378,20 @@ class MemoryBuffer {
copy_vn_fl(result, this->m_num_channels, 0.0f);
return;
}
- BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
- result,
- getWidth(),
- getHeight(),
- this->m_num_channels,
- u,
- v,
- extend_x == MemoryBufferExtend::Repeat,
- extend_y == MemoryBufferExtend::Repeat);
+ if (m_is_a_single_elem) {
+ memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
+ }
+ else {
+ BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
+ result,
+ getWidth(),
+ getHeight(),
+ this->m_num_channels,
+ u,
+ v,
+ extend_x == MemoryBufferExtend::Repeat,
+ extend_y == MemoryBufferExtend::Repeat);
+ }
}
void readEWA(float *result, const float uv[2], const float derivatives[2][2]);
@@ -322,12 +446,15 @@ class MemoryBuffer {
float get_max_value(const rcti &rect) const;
private:
+ void set_strides();
const int buffer_len() const
{
- return getWidth() * getHeight();
+ return get_memory_width() * get_memory_height();
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cc b/source/blender/compositor/intern/COM_MemoryProxy.cc
index 8ef834e1efe..6023850c944 100644
--- a/source/blender/compositor/intern/COM_MemoryProxy.cc
+++ b/source/blender/compositor/intern/COM_MemoryProxy.cc
@@ -18,6 +18,12 @@
#include "COM_MemoryProxy.h"
+#include "COM_MemoryBuffer.h"
+
+#include "BLI_rect.h"
+
+namespace blender::compositor {
+
MemoryProxy::MemoryProxy(DataType datatype)
{
this->m_writeBufferOperation = nullptr;
@@ -43,3 +49,5 @@ void MemoryProxy::free()
this->m_buffer = nullptr;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h
index ee98ff41630..6814afada74 100644
--- a/source/blender/compositor/intern/COM_MemoryProxy.h
+++ b/source/blender/compositor/intern/COM_MemoryProxy.h
@@ -16,13 +16,18 @@
* Copyright 2011, Blender Foundation.
*/
-class MemoryProxy;
-
#pragma once
-#include "COM_ExecutionGroup.h"
-#include "COM_MemoryBuffer.h"
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+#include "COM_defines.h"
+
+namespace blender::compositor {
+/* Forward declarations. */
+class MemoryBuffer;
class ExecutionGroup;
class WriteBufferOperation;
@@ -69,7 +74,7 @@ class MemoryProxy {
/**
* \brief get the ExecutionGroup that can be scheduled to calculate a certain chunk.
*/
- ExecutionGroup *getExecutor()
+ ExecutionGroup *getExecutor() const
{
return this->m_executor;
}
@@ -87,7 +92,7 @@ class MemoryProxy {
* \brief get the WriteBufferOperation that is responsible for writing to this MemoryProxy
* \return WriteBufferOperation
*/
- WriteBufferOperation *getWriteBufferOperation()
+ WriteBufferOperation *getWriteBufferOperation() const
{
return this->m_writeBufferOperation;
}
@@ -119,3 +124,5 @@ class MemoryProxy {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryProxy")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MetaData.cc b/source/blender/compositor/intern/COM_MetaData.cc
index ad72b242c8c..88bfa385514 100644
--- a/source/blender/compositor/intern/COM_MetaData.cc
+++ b/source/blender/compositor/intern/COM_MetaData.cc
@@ -24,6 +24,8 @@
#include <string_view>
+namespace blender::compositor {
+
void MetaData::add(const blender::StringRef key, const blender::StringRef value)
{
entries_.add(key, value);
@@ -64,7 +66,7 @@ void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_
void MetaData::addToRenderResult(RenderResult *render_result) const
{
- for (blender::Map<std::string, std::string>::Item entry : entries_.items()) {
+ for (Map<std::string, std::string>::Item entry : entries_.items()) {
BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str());
}
}
@@ -104,3 +106,5 @@ void MetaDataExtractCallbackData::extract_cryptomatte_meta_data(void *_data,
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MetaData.h b/source/blender/compositor/intern/COM_MetaData.h
index fa3de895b4e..a76540dc3af 100644
--- a/source/blender/compositor/intern/COM_MetaData.h
+++ b/source/blender/compositor/intern/COM_MetaData.h
@@ -28,6 +28,8 @@
/* Forward declarations. */
struct RenderResult;
+namespace blender::compositor {
+
/* Cryptomatte includes hash in its meta data keys. The hash is generated from the render
* layer/pass name. Compositing happens without the knowledge of the original layer and pass. The
* next keys are used to transfer the cryptomatte meta data in a neutral way. The file output node
@@ -41,7 +43,7 @@ constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_NAME("cryptomatte/{hash}/
class MetaData {
private:
- blender::Map<std::string, std::string> entries_;
+ Map<std::string, std::string> entries_;
void addCryptomatteEntry(const blender::StringRef layer_name,
const blender::StringRefNull key,
const blender::StringRef value);
@@ -69,3 +71,5 @@ struct MetaDataExtractCallbackData {
char *propvalue,
int UNUSED(len));
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
new file mode 100644
index 00000000000..c54c2edccb0
--- /dev/null
+++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
@@ -0,0 +1,26 @@
+#include "COM_MultiThreadedOperation.h"
+#include "COM_ExecutionSystem.h"
+
+namespace blender::compositor {
+
+MultiThreadedOperation::MultiThreadedOperation()
+{
+ m_num_passes = 1;
+ flags.is_fullframe_operation = true;
+}
+
+void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output,
+ const rcti &output_area,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system)
+{
+ for (int current_pass = 0; current_pass < m_num_passes; current_pass++) {
+ update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass);
+ exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) {
+ update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass);
+ });
+ update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h
new file mode 100644
index 00000000000..97c5fba4ead
--- /dev/null
+++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h
@@ -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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+class MultiThreadedOperation : public NodeOperation {
+ protected:
+ /**
+ * Number of execution passes.
+ */
+ int m_num_passes;
+
+ protected:
+ MultiThreadedOperation();
+
+ /**
+ * Called before an update memory buffer pass is executed. Single-threaded calls.
+ */
+ virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_rect),
+ blender::Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system),
+ int UNUSED(current_pass))
+ {
+ }
+
+ /**
+ * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls.
+ */
+ virtual void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &output_rect,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system,
+ int current_pass) = 0;
+
+ /**
+ * Called after an update memory buffer pass is executed. Single-threaded calls.
+ */
+ virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_rect),
+ blender::Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system),
+ int UNUSED(current_pass))
+ {
+ }
+
+ private:
+ void update_memory_buffer(MemoryBuffer *output,
+ const rcti &output_area,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Node.cc b/source/blender/compositor/intern/COM_Node.cc
index 819f1c02b14..6ac48e3646c 100644
--- a/source/blender/compositor/intern/COM_Node.cc
+++ b/source/blender/compositor/intern/COM_Node.cc
@@ -32,6 +32,8 @@
#include "COM_Node.h" /* own include */
+namespace blender::compositor {
+
/**************
**** Node ****
**************/
@@ -74,13 +76,11 @@ Node::Node(bNode *editorNode, bool create_sockets)
Node::~Node()
{
- while (!this->m_outputsockets.empty()) {
- delete (this->m_outputsockets.back());
- this->m_outputsockets.pop_back();
+ while (!this->outputs.is_empty()) {
+ delete (this->outputs.pop_last());
}
- while (!this->m_inputsockets.empty()) {
- delete (this->m_inputsockets.back());
- this->m_inputsockets.pop_back();
+ while (!this->inputs.is_empty()) {
+ delete (this->inputs.pop_last());
}
}
@@ -92,7 +92,7 @@ void Node::addInputSocket(DataType datatype)
void Node::addInputSocket(DataType datatype, bNodeSocket *bSocket)
{
NodeInput *socket = new NodeInput(this, bSocket, datatype);
- this->m_inputsockets.push_back(socket);
+ this->inputs.append(socket);
}
void Node::addOutputSocket(DataType datatype)
@@ -102,19 +102,17 @@ void Node::addOutputSocket(DataType datatype)
void Node::addOutputSocket(DataType datatype, bNodeSocket *bSocket)
{
NodeOutput *socket = new NodeOutput(this, bSocket, datatype);
- this->m_outputsockets.push_back(socket);
+ outputs.append(socket);
}
NodeOutput *Node::getOutputSocket(unsigned int index) const
{
- BLI_assert(index < this->m_outputsockets.size());
- return this->m_outputsockets[index];
+ return outputs[index];
}
NodeInput *Node::getInputSocket(unsigned int index) const
{
- BLI_assert(index < this->m_inputsockets.size());
- return this->m_inputsockets[index];
+ return inputs[index];
}
bNodeSocket *Node::getEditorInputSocket(int editorNodeInputSocketIndex)
@@ -208,3 +206,5 @@ void NodeOutput::getEditorValueVector(float *value)
RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr);
return RNA_float_get_array(&ptr, "default_value", value);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h
index 99f1b58b5ef..28b59397af9 100644
--- a/source/blender/compositor/intern/COM_Node.h
+++ b/source/blender/compositor/intern/COM_Node.h
@@ -18,10 +18,12 @@
#pragma once
+#include "BLI_vector.hh"
+
#include "DNA_node_types.h"
+
#include <algorithm>
#include <string>
-#include <vector>
/* common node includes
* added here so node files don't have to include themselves
@@ -29,7 +31,8 @@
#include "COM_CompositorContext.h"
#include "COM_NodeConverter.h"
-class Node;
+namespace blender::compositor {
+
class NodeOperation;
class NodeConverter;
@@ -37,10 +40,6 @@ class NodeConverter;
* My node documentation.
*/
class Node {
- public:
- typedef std::vector<NodeInput *> Inputs;
- typedef std::vector<NodeOutput *> Outputs;
-
private:
/**
* \brief stores the reference to the SDNA bNode struct
@@ -53,16 +52,6 @@ class Node {
bNode *m_editorNode;
/**
- * \brief the list of actual inputsockets \see NodeInput
- */
- Inputs m_inputsockets;
-
- /**
- * \brief the list of actual outputsockets \see NodeOutput
- */
- Outputs m_outputsockets;
-
- /**
* \brief Is this node part of the active group
*/
bool m_inActiveGroup;
@@ -74,20 +63,14 @@ class Node {
protected:
/**
- * \brief get access to the vector of input sockets
+ * \brief the list of actual input-sockets \see NodeInput
*/
- const Inputs &getInputSockets() const
- {
- return this->m_inputsockets;
- }
+ Vector<NodeInput *> inputs;
/**
- * \brief get access to the vector of input sockets
+ * \brief the list of actual output-sockets \see NodeOutput
*/
- const Outputs &getOutputSockets() const
- {
- return this->m_outputsockets;
- }
+ Vector<NodeOutput *> outputs;
public:
Node(bNode *editorNode, bool create_sockets = true);
@@ -130,19 +113,19 @@ class Node {
}
/**
- * \brief Return the number of input sockets of this node.
+ * \brief get access to the vector of input sockets
*/
- unsigned int getNumberOfInputSockets() const
+ const Vector<NodeInput *> &getInputSockets() const
{
- return this->m_inputsockets.size();
+ return this->inputs;
}
/**
- * \brief Return the number of output sockets of this node.
+ * \brief get access to the vector of input sockets
*/
- unsigned int getNumberOfOutputSockets() const
+ const Vector<NodeOutput *> &getOutputSockets() const
{
- return this->m_outputsockets.size();
+ return this->outputs;
}
/**
@@ -150,17 +133,7 @@ class Node {
* \param index:
* the index of the needed outputsocket
*/
- NodeOutput *getOutputSocket(const unsigned int index) const;
-
- /**
- * get the reference to the first outputsocket
- * \param index:
- * the index of the needed outputsocket
- */
- inline NodeOutput *getOutputSocket() const
- {
- return getOutputSocket(0);
- }
+ NodeOutput *getOutputSocket(const unsigned int index = 0) const;
/**
* get the reference to a certain inputsocket
@@ -169,14 +142,6 @@ class Node {
*/
NodeInput *getInputSocket(const unsigned int index) const;
- /** Check if this is an input node
- * An input node is a node that only has output sockets and no input sockets
- */
- bool isInputNode() const
- {
- return m_inputsockets.empty();
- }
-
/**
* \brief Is this node in the active group (the group that is being edited)
* \param isInActiveGroup:
@@ -219,7 +184,7 @@ class Node {
protected:
/**
- * \brief add an NodeInput to the collection of inputsockets
+ * \brief add an NodeInput to the collection of input-sockets
* \note may only be called in an constructor
* \param socket: the NodeInput to add
*/
@@ -227,7 +192,7 @@ class Node {
void addInputSocket(DataType datatype, bNodeSocket *socket);
/**
- * \brief add an NodeOutput to the collection of outputsockets
+ * \brief add an NodeOutput to the collection of output-sockets
* \note may only be called in an constructor
* \param socket: the NodeOutput to add
*/
@@ -317,3 +282,5 @@ class NodeOutput {
void getEditorValueColor(float *value);
void getEditorValueVector(float *value);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeConverter.cc b/source/blender/compositor/intern/COM_NodeConverter.cc
index 2db31bd4133..49a2e7988c4 100644
--- a/source/blender/compositor/intern/COM_NodeConverter.cc
+++ b/source/blender/compositor/intern/COM_NodeConverter.cc
@@ -29,6 +29,8 @@
#include "COM_NodeConverter.h" /* own include */
+namespace blender::compositor {
+
NodeConverter::NodeConverter(NodeOperationBuilder *builder) : m_builder(builder)
{
}
@@ -160,3 +162,5 @@ ViewerOperation *NodeConverter::active_viewer() const
{
return m_builder->active_viewer();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeConverter.h b/source/blender/compositor/intern/COM_NodeConverter.h
index e9b05184857..b3f03485249 100644
--- a/source/blender/compositor/intern/COM_NodeConverter.h
+++ b/source/blender/compositor/intern/COM_NodeConverter.h
@@ -22,6 +22,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
class NodeInput;
class NodeOutput;
@@ -120,3 +122,5 @@ class NodeConverter {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompiler")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc
index d8220099f1f..fbe56dd4b5a 100644
--- a/source/blender/compositor/intern/COM_NodeGraph.cc
+++ b/source/blender/compositor/intern/COM_NodeGraph.cc
@@ -33,19 +33,16 @@
#include "COM_NodeGraph.h" /* own include */
+namespace blender::compositor {
+
/*******************
**** NodeGraph ****
*******************/
-NodeGraph::NodeGraph()
-{
-}
-
NodeGraph::~NodeGraph()
{
- for (int index = 0; index < this->m_nodes.size(); index++) {
- Node *node = this->m_nodes[index];
- delete node;
+ while (m_nodes.size()) {
+ delete m_nodes.pop_last();
}
}
@@ -83,7 +80,7 @@ void NodeGraph::add_node(Node *node,
node->setInstanceKey(key);
node->setIsInActiveGroup(is_active_group);
- m_nodes.push_back(node);
+ m_nodes.append(node);
DebugInfo::node_added(node);
}
@@ -153,27 +150,11 @@ void NodeGraph::add_bNode(const CompositorContext &context,
}
}
-NodeGraph::NodeInputs NodeGraph::find_inputs(const NodeRange &node_range, bNodeSocket *b_socket)
-{
- NodeInputs result;
- for (NodeGraph::NodeIterator it = node_range.first; it != node_range.second; ++it) {
- Node *node = *it;
- for (int index = 0; index < node->getNumberOfInputSockets(); index++) {
- NodeInput *input = node->getInputSocket(index);
- if (input->getbNodeSocket() == b_socket) {
- result.push_back(input);
- }
- }
- }
- return result;
-}
-
NodeOutput *NodeGraph::find_output(const NodeRange &node_range, bNodeSocket *b_socket)
{
- for (NodeGraph::NodeIterator it = node_range.first; it != node_range.second; ++it) {
+ for (Vector<Node *>::iterator it = node_range.first; it != node_range.second; ++it) {
Node *node = *it;
- for (int index = 0; index < node->getNumberOfOutputSockets(); index++) {
- NodeOutput *output = node->getOutputSocket(index);
+ for (NodeOutput *output : node->getOutputSockets()) {
if (output->getbNodeSocket() == b_socket) {
return output;
}
@@ -202,12 +183,13 @@ void NodeGraph::add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink
return;
}
- NodeInputs inputs = find_inputs(node_range, b_nodelink->tosock);
- for (NodeInput *input : inputs) {
- if (input->isLinked()) {
- continue;
+ for (Vector<Node *>::iterator it = node_range.first; it != node_range.second; ++it) {
+ Node *node = *it;
+ for (NodeInput *input : node->getInputSockets()) {
+ if (input->getbNodeSocket() == b_nodelink->tosock && !input->isLinked()) {
+ add_link(output, input);
+ }
}
- add_link(output, input);
}
}
@@ -331,3 +313,5 @@ void NodeGraph::add_proxies_reroute(bNodeTree *b_ntree,
b_node, (bNodeSocket *)b_node->inputs.first, (bNodeSocket *)b_node->outputs.first, false);
add_node(proxy, b_ntree, key, is_active_group);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h
index 990e3a30831..7fa01593e1e 100644
--- a/source/blender/compositor/intern/COM_NodeGraph.h
+++ b/source/blender/compositor/intern/COM_NodeGraph.h
@@ -22,7 +22,6 @@
#include <map>
#include <set>
-#include <vector>
#include "DNA_node_types.h"
@@ -30,6 +29,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
class CompositorContext;
class Node;
class NodeInput;
@@ -50,22 +51,18 @@ class NodeGraph {
}
};
- typedef std::vector<Node *> Nodes;
- typedef Nodes::iterator NodeIterator;
-
private:
- Nodes m_nodes;
- blender::Vector<Link> m_links;
+ Vector<Node *> m_nodes;
+ Vector<Link> m_links;
public:
- NodeGraph();
~NodeGraph();
- const Nodes &nodes() const
+ const Vector<Node *> &nodes() const
{
return m_nodes;
}
- const blender::Vector<Link> &links() const
+ const Vector<Link> &links() const
{
return m_links;
}
@@ -73,8 +70,7 @@ class NodeGraph {
void from_bNodeTree(const CompositorContext &context, bNodeTree *tree);
protected:
- typedef std::pair<NodeIterator, NodeIterator> NodeRange;
- typedef std::vector<NodeInput *> NodeInputs;
+ typedef std::pair<Vector<Node *>::iterator, Vector<Node *>::iterator> NodeRange;
static bNodeSocket *find_b_node_input(bNode *b_node, const char *identifier);
static bNodeSocket *find_b_node_output(bNode *b_node, const char *identifier);
@@ -93,7 +89,6 @@ class NodeGraph {
bNodeInstanceKey key,
bool is_active_group);
- NodeInputs find_inputs(const NodeRange &node_range, bNodeSocket *b_socket);
NodeOutput *find_output(const NodeRange &node_range, bNodeSocket *b_socket);
void add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink);
@@ -124,3 +119,5 @@ class NodeGraph {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeGraph")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc
index 0cc642479ac..83de8a751c4 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -17,13 +17,18 @@
*/
#include <cstdio>
+#include <memory>
#include <typeinfo>
+#include "COM_BufferOperation.h"
#include "COM_ExecutionSystem.h"
+#include "COM_ReadBufferOperation.h"
#include "COM_defines.h"
#include "COM_NodeOperation.h" /* own include */
+namespace blender::compositor {
+
/*******************
**** NodeOperation ****
*******************/
@@ -31,76 +36,66 @@
NodeOperation::NodeOperation()
{
this->m_resolutionInputSocketIndex = 0;
- this->m_complex = false;
this->m_width = 0;
this->m_height = 0;
- this->m_isResolutionSet = false;
- this->m_openCL = false;
this->m_btree = nullptr;
}
-NodeOperation::~NodeOperation()
+NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index)
{
- while (!this->m_outputs.empty()) {
- delete (this->m_outputs.back());
- this->m_outputs.pop_back();
- }
- while (!this->m_inputs.empty()) {
- delete (this->m_inputs.back());
- this->m_inputs.pop_back();
- }
+ return &m_outputs[index];
}
-NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index) const
+NodeOperationInput *NodeOperation::getInputSocket(unsigned int index)
{
- BLI_assert(index < m_outputs.size());
- return m_outputs[index];
+ return &m_inputs[index];
}
-NodeOperationInput *NodeOperation::getInputSocket(unsigned int index) const
+void NodeOperation::addInputSocket(DataType datatype, ResizeMode resize_mode)
{
- BLI_assert(index < m_inputs.size());
- return m_inputs[index];
-}
-
-void NodeOperation::addInputSocket(DataType datatype, InputResizeMode resize_mode)
-{
- NodeOperationInput *socket = new NodeOperationInput(this, datatype, resize_mode);
- m_inputs.push_back(socket);
+ m_inputs.append(NodeOperationInput(this, datatype, resize_mode));
}
void NodeOperation::addOutputSocket(DataType datatype)
{
- NodeOperationOutput *socket = new NodeOperationOutput(this, datatype);
- m_outputs.push_back(socket);
+ m_outputs.append(NodeOperationOutput(this, datatype));
}
void NodeOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
- unsigned int temp[2];
- unsigned int temp2[2];
-
- for (unsigned int index = 0; index < m_inputs.size(); index++) {
- NodeOperationInput *input = m_inputs[index];
- if (input->isConnected()) {
- if (index == this->m_resolutionInputSocketIndex) {
- input->determineResolution(resolution, preferredResolution);
- temp2[0] = resolution[0];
- temp2[1] = resolution[1];
+ unsigned int used_resolution_index = 0;
+ if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) {
+ for (NodeOperationInput &input : m_inputs) {
+ unsigned int any_resolution[2] = {0, 0};
+ input.determineResolution(any_resolution, preferredResolution);
+ if (any_resolution[0] * any_resolution[1] > 0) {
+ resolution[0] = any_resolution[0];
+ resolution[1] = any_resolution[1];
break;
}
+ used_resolution_index += 1;
}
}
+ else if (m_resolutionInputSocketIndex < m_inputs.size()) {
+ NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex];
+ input.determineResolution(resolution, preferredResolution);
+ used_resolution_index = m_resolutionInputSocketIndex;
+ }
+ unsigned int temp2[2] = {resolution[0], resolution[1]};
+
+ unsigned int temp[2];
for (unsigned int index = 0; index < m_inputs.size(); index++) {
- NodeOperationInput *input = m_inputs[index];
- if (input->isConnected()) {
- if (index != this->m_resolutionInputSocketIndex) {
- input->determineResolution(temp, temp2);
- }
+ if (index == used_resolution_index) {
+ continue;
+ }
+ NodeOperationInput &input = m_inputs[index];
+ if (input.isConnected()) {
+ input.determineResolution(temp, temp2);
}
}
}
+
void NodeOperation::setResolutionInputSocketIndex(unsigned int index)
{
this->m_resolutionInputSocketIndex = index;
@@ -149,20 +144,11 @@ NodeOperation *NodeOperation::getInputOperation(unsigned int inputSocketIndex)
return nullptr;
}
-void NodeOperation::getConnectedInputSockets(Inputs *sockets)
-{
- for (NodeOperationInput *input : m_inputs) {
- if (input->isConnected()) {
- sockets->push_back(input);
- }
- }
-}
-
bool NodeOperation::determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output)
{
- if (isInputOperation()) {
+ if (m_inputs.size() == 0) {
BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax);
return false;
}
@@ -191,13 +177,182 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input,
return !first;
}
+/* -------------------------------------------------------------------- */
+/** \name Full Frame Methods
+ * \{ */
+
+/**
+ * \brief Get input operation area being read by this operation on rendering given output area.
+ *
+ * Implementation don't need to ensure r_input_area is within input operation bounds. The
+ * caller must clamp it.
+ * TODO: See if it's possible to use parameter overloading (input_id for example).
+ *
+ * \param input_op_idx: Input operation index for which we want to calculate the area being read.
+ * \param output_area: Area being rendered by this operation.
+ * \param r_input_area: Returned input operation area that needs to be read in order to render
+ * given output area.
+ */
+void NodeOperation::get_area_of_interest(const int input_op_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (get_flags().is_fullframe_operation) {
+ r_input_area = output_area;
+ }
+ else {
+ /* Non full-frame operations never implement this method. To ensure correctness assume
+ * whole area is used. */
+ NodeOperation *input_op = getInputOperation(input_op_idx);
+ BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight());
+ }
+}
+
+void NodeOperation::get_area_of_interest(NodeOperation *input_op,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ for (int i = 0; i < getNumberOfInputSockets(); i++) {
+ if (input_op == getInputOperation(i)) {
+ get_area_of_interest(i, output_area, r_input_area);
+ return;
+ }
+ }
+ BLI_assert(!"input_op is not an input operation.");
+}
+
+/**
+ * Executes operation image manipulation algorithm rendering given areas.
+ * \param output_buf: Buffer to write result to.
+ * \param areas: Areas within this operation bounds to render.
+ * \param inputs_bufs: Inputs operations buffers.
+ * \param exec_system: Execution system.
+ */
+void NodeOperation::render(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ if (get_flags().is_fullframe_operation) {
+ render_full_frame(output_buf, areas, inputs_bufs, exec_system);
+ }
+ else {
+ render_full_frame_fallback(output_buf, areas, inputs_bufs, exec_system);
+ }
+}
+
+/**
+ * Renders given areas using operations full frame implementation.
+ */
+void NodeOperation::render_full_frame(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ initExecution();
+ for (const rcti &area : areas) {
+ update_memory_buffer(output_buf, area, inputs_bufs, exec_system);
+ }
+ deinitExecution();
+}
+
+/**
+ * Renders given areas using operations tiled implementation.
+ */
+void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ Vector<NodeOperationOutput *> orig_input_links = replace_inputs_with_buffers(inputs_bufs);
+
+ initExecution();
+ const bool is_output_operation = getNumberOfOutputSockets() == 0;
+ if (!is_output_operation && output_buf->is_a_single_elem()) {
+ float *output_elem = output_buf->get_elem(0, 0);
+ readSampled(output_elem, 0, 0, PixelSampler::Nearest);
+ }
+ else {
+ for (const rcti &rect : areas) {
+ exec_system.execute_work(rect, [=](const rcti &split_rect) {
+ rcti tile_rect = split_rect;
+ if (is_output_operation) {
+ executeRegion(&tile_rect, 0);
+ }
+ else {
+ render_tile(output_buf, &tile_rect);
+ }
+ });
+ }
+ }
+ deinitExecution();
+
+ remove_buffers_and_restore_original_inputs(orig_input_links);
+}
+
+void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect)
+{
+ const bool is_complex = get_flags().complex;
+ void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr;
+ const int elem_stride = output_buf->elem_stride;
+ for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) {
+ float *output_elem = output_buf->get_elem(tile_rect->xmin, y);
+ if (is_complex) {
+ for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) {
+ read(output_elem, x, y, tile_data);
+ output_elem += elem_stride;
+ }
+ }
+ else {
+ for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) {
+ readSampled(output_elem, x, y, PixelSampler::Nearest);
+ output_elem += elem_stride;
+ }
+ }
+ }
+ if (tile_data) {
+ deinitializeTileData(tile_rect, tile_data);
+ }
+}
+
+/**
+ * \return Replaced inputs links.
+ */
+Vector<NodeOperationOutput *> NodeOperation::replace_inputs_with_buffers(
+ Span<MemoryBuffer *> inputs_bufs)
+{
+ BLI_assert(inputs_bufs.size() == getNumberOfInputSockets());
+ Vector<NodeOperationOutput *> orig_links(inputs_bufs.size());
+ for (int i = 0; i < inputs_bufs.size(); i++) {
+ NodeOperationInput *input_socket = getInputSocket(i);
+ BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType());
+ orig_links[i] = input_socket->getLink();
+ input_socket->setLink(buffer_op->getOutputSocket());
+ }
+ return orig_links;
+}
+
+void NodeOperation::remove_buffers_and_restore_original_inputs(
+ Span<NodeOperationOutput *> original_inputs_links)
+{
+ BLI_assert(original_inputs_links.size() == getNumberOfInputSockets());
+ for (int i = 0; i < original_inputs_links.size(); i++) {
+ NodeOperation *buffer_op = get_input_operation(i);
+ BLI_assert(buffer_op != nullptr);
+ BLI_assert(typeid(*buffer_op) == typeid(BufferOperation));
+ NodeOperationInput *input_socket = getInputSocket(i);
+ input_socket->setLink(original_inputs_links[i]);
+ delete buffer_op;
+ }
+}
+
+/** \} */
+
/*****************
**** OpInput ****
*****************/
-NodeOperationInput::NodeOperationInput(NodeOperation *op,
- DataType datatype,
- InputResizeMode resizeMode)
+NodeOperationInput::NodeOperationInput(NodeOperation *op, DataType datatype, ResizeMode resizeMode)
: m_operation(op), m_datatype(datatype), m_resizeMode(resizeMode), m_link(nullptr)
{
}
@@ -232,12 +387,88 @@ void NodeOperationOutput::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
NodeOperation &operation = getOperation();
- if (operation.isResolutionSet()) {
+ if (operation.get_flags().is_resolution_set) {
resolution[0] = operation.getWidth();
resolution[1] = operation.getHeight();
}
else {
operation.determineResolution(resolution, preferredResolution);
- operation.setResolution(resolution);
+ if (resolution[0] > 0 && resolution[1] > 0) {
+ operation.setResolution(resolution);
+ }
+ }
+}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operation_flags)
+{
+ if (node_operation_flags.complex) {
+ os << "complex,";
+ }
+ if (node_operation_flags.open_cl) {
+ os << "open_cl,";
+ }
+ if (node_operation_flags.single_threaded) {
+ os << "single_threaded,";
+ }
+ if (node_operation_flags.use_render_border) {
+ os << "render_border,";
+ }
+ if (node_operation_flags.use_viewer_border) {
+ os << "view_border,";
+ }
+ if (node_operation_flags.is_resolution_set) {
+ os << "resolution_set,";
+ }
+ if (node_operation_flags.is_set_operation) {
+ os << "set_operation,";
+ }
+ if (node_operation_flags.is_write_buffer_operation) {
+ os << "write_buffer,";
+ }
+ if (node_operation_flags.is_read_buffer_operation) {
+ os << "read_buffer,";
+ }
+ if (node_operation_flags.is_proxy_operation) {
+ os << "proxy,";
+ }
+ if (node_operation_flags.is_viewer_operation) {
+ os << "viewer,";
}
+ if (node_operation_flags.is_preview_operation) {
+ os << "preview,";
+ }
+ if (!node_operation_flags.use_datatype_conversion) {
+ os << "no_conversion,";
+ }
+ if (node_operation_flags.is_fullframe_operation) {
+ os << "full_frame,";
+ }
+
+ return os;
}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation)
+{
+ NodeOperationFlags flags = node_operation.get_flags();
+ os << "NodeOperation(";
+ os << "id=" << node_operation.get_id();
+ if (!node_operation.get_name().empty()) {
+ os << ",name=" << node_operation.get_name();
+ }
+ os << ",flags={" << flags << "}";
+ if (flags.is_read_buffer_operation) {
+ const ReadBufferOperation *read_operation = (const ReadBufferOperation *)&node_operation;
+ const MemoryProxy *proxy = read_operation->getMemoryProxy();
+ if (proxy) {
+ const WriteBufferOperation *write_operation = proxy->getWriteBufferOperation();
+ if (write_operation) {
+ os << ",write=" << (NodeOperation &)*write_operation;
+ }
+ }
+ }
+ os << ")";
+
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index 7857837a95d..5c4d6dd19ba 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -26,77 +26,258 @@
#include "BLI_math_vector.h"
#include "BLI_threads.h"
+#include "COM_Enums.h"
#include "COM_MemoryBuffer.h"
#include "COM_MemoryProxy.h"
+#include "COM_MetaData.h"
#include "COM_Node.h"
-#include "COM_SocketReader.h"
#include "clew.h"
+namespace blender::compositor {
+
class OpenCLDevice;
class ReadBufferOperation;
class WriteBufferOperation;
+class ExecutionSystem;
+
+class NodeOperation;
+typedef NodeOperation SocketReader;
-class NodeOperationInput;
-class NodeOperationOutput;
+/**
+ * RESOLUTION_INPUT_ANY is a wildcard when any resolution of an input can be used.
+ * This solves the issue that the FileInputNode in a group node cannot find the
+ * correct resolution.
+ */
+static constexpr unsigned int RESOLUTION_INPUT_ANY = 999999;
/**
* \brief Resize modes of inputsockets
* How are the input and working resolutions matched
* \ingroup Model
*/
-typedef enum InputResizeMode {
+enum class ResizeMode {
/** \brief Center the input image to the center of the working area of the node, no resizing
* occurs */
- COM_SC_CENTER = NS_CR_CENTER,
+ Center = NS_CR_CENTER,
/** \brief The bottom left of the input image is the bottom left of the working area of the node,
* no resizing occurs */
- COM_SC_NO_RESIZE = NS_CR_NONE,
+ None = NS_CR_NONE,
/** \brief Fit the width of the input image to the width of the working area of the node */
- COM_SC_FIT_WIDTH = NS_CR_FIT_WIDTH,
+ FitWidth = NS_CR_FIT_WIDTH,
/** \brief Fit the height of the input image to the height of the working area of the node */
- COM_SC_FIT_HEIGHT = NS_CR_FIT_HEIGHT,
+ FitHeight = NS_CR_FIT_HEIGHT,
/** \brief Fit the width or the height of the input image to the width or height of the working
* area of the node, image will be larger than the working area */
- COM_SC_FIT = NS_CR_FIT,
+ FitAny = NS_CR_FIT,
/** \brief Fit the width and the height of the input image to the width and height of the working
* area of the node, image will be equally larger than the working area */
- COM_SC_STRETCH = NS_CR_STRETCH,
-} InputResizeMode;
+ Stretch = NS_CR_STRETCH,
+};
+
+enum class PixelSampler {
+ Nearest = 0,
+ Bilinear = 1,
+ Bicubic = 2,
+};
+
+class NodeOperationInput {
+ private:
+ NodeOperation *m_operation;
+
+ /** Datatype of this socket. Is used for automatically data transformation.
+ * \section data-conversion
+ */
+ DataType m_datatype;
+
+ /** Resize mode of this socket */
+ ResizeMode m_resizeMode;
+
+ /** Connected output */
+ NodeOperationOutput *m_link;
-/**
- * \brief NodeOperation contains calculation logic
- *
- * Subclasses needs to implement the execution method (defined in SocketReader) to implement logic.
- * \ingroup Model
- */
-class NodeOperation : public SocketReader {
public:
- typedef std::vector<NodeOperationInput *> Inputs;
- typedef std::vector<NodeOperationOutput *> Outputs;
+ NodeOperationInput(NodeOperation *op,
+ DataType datatype,
+ ResizeMode resizeMode = ResizeMode::Center);
+ NodeOperation &getOperation() const
+ {
+ return *m_operation;
+ }
+ DataType getDataType() const
+ {
+ return m_datatype;
+ }
+
+ void setLink(NodeOperationOutput *link)
+ {
+ m_link = link;
+ }
+ NodeOperationOutput *getLink() const
+ {
+ return m_link;
+ }
+ bool isConnected() const
+ {
+ return m_link;
+ }
+
+ void setResizeMode(ResizeMode resizeMode)
+ {
+ this->m_resizeMode = resizeMode;
+ }
+ ResizeMode getResizeMode() const
+ {
+ return this->m_resizeMode;
+ }
+
+ SocketReader *getReader();
+
+ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
+#endif
+};
+
+class NodeOperationOutput {
private:
- Inputs m_inputs;
- Outputs m_outputs;
+ NodeOperation *m_operation;
+
+ /** Datatype of this socket. Is used for automatically data transformation.
+ * \section data-conversion
+ */
+ DataType m_datatype;
+
+ public:
+ NodeOperationOutput(NodeOperation *op, DataType datatype);
+
+ NodeOperation &getOperation() const
+ {
+ return *m_operation;
+ }
+ DataType getDataType() const
+ {
+ return m_datatype;
+ }
/**
- * \brief the index of the input socket that will be used to determine the resolution
+ * \brief determine the resolution of this data going through this socket
+ * \param resolution: the result of this operation
+ * \param preferredResolution: the preferable resolution as no resolution could be determined
*/
- unsigned int m_resolutionInputSocketIndex;
+ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
+#endif
+};
+
+struct NodeOperationFlags {
/**
- * \brief is this operation a complex one.
+ * Is this an complex operation.
+ *
+ * The input and output buffers of Complex operations are stored in buffers. It allows
+ * sequential and read/write.
*
* Complex operations are typically doing many reads to calculate the output of a single pixel.
* Mostly Filter types (Blurs, Convolution, Defocus etc) need this to be set to true.
*/
- bool m_complex;
+ bool complex : 1;
+
+ /**
+ * Does this operation support OpenCL.
+ */
+ bool open_cl : 1;
+
+ /**
+ * TODO: Remove this flag and #SingleThreadedOperation if tiled implementation is removed.
+ * Full-frame implementation doesn't need it.
+ */
+ bool single_threaded : 1;
/**
- * \brief can this operation be scheduled on an OpenCL device.
- * \note Only applicable if complex is True
+ * Does the operation needs a viewer border.
+ * Basically, setting border need to happen for only operations
+ * which operates in render resolution buffers (like compositor
+ * output nodes).
+ *
+ * In this cases adding border will lead to mapping coordinates
+ * from output buffer space to input buffer spaces when executing
+ * operation.
+ *
+ * But nodes like viewer and file output just shall display or
+ * safe the same exact buffer which goes to their input, no need
+ * in any kind of coordinates mapping.
+ */
+ bool use_render_border : 1;
+ bool use_viewer_border : 1;
+
+ /**
+ * Is the resolution of the operation set.
*/
- bool m_openCL;
+ bool is_resolution_set : 1;
+
+ /**
+ * Is this a set operation (value, color, vector).
+ */
+ bool is_set_operation : 1;
+ bool is_write_buffer_operation : 1;
+ bool is_read_buffer_operation : 1;
+ bool is_proxy_operation : 1;
+ bool is_viewer_operation : 1;
+ bool is_preview_operation : 1;
+
+ /**
+ * When set additional data conversion operations are added to
+ * convert the data. SocketProxyOperation don't always need to do data conversions.
+ *
+ * By default data conversions are enabled.
+ */
+ bool use_datatype_conversion : 1;
+
+ /**
+ * Has this operation fullframe implementation.
+ */
+ bool is_fullframe_operation : 1;
+
+ NodeOperationFlags()
+ {
+ complex = false;
+ single_threaded = false;
+ open_cl = false;
+ use_render_border = false;
+ use_viewer_border = false;
+ is_resolution_set = false;
+ is_set_operation = false;
+ is_read_buffer_operation = false;
+ is_write_buffer_operation = false;
+ is_proxy_operation = false;
+ is_viewer_operation = false;
+ is_preview_operation = false;
+ use_datatype_conversion = true;
+ is_fullframe_operation = false;
+ }
+};
+
+/**
+ * \brief NodeOperation contains calculation logic
+ *
+ * Subclasses needs to implement the execution method (defined in SocketReader) to implement logic.
+ * \ingroup Model
+ */
+class NodeOperation {
+ private:
+ int m_id;
+ std::string m_name;
+ Vector<NodeOperationInput> m_inputs;
+ Vector<NodeOperationOutput> m_outputs;
+
+ /**
+ * \brief the index of the input socket that will be used to determine the resolution
+ */
+ unsigned int m_resolutionInputSocketIndex;
/**
* \brief mutex reference for very special node initializations
@@ -114,13 +295,61 @@ class NodeOperation : public SocketReader {
*/
const bNodeTree *m_btree;
+ protected:
/**
- * \brief set to truth when resolution for this operation is set
+ * Compositor execution model.
*/
- bool m_isResolutionSet;
+ eExecutionModel execution_model_;
+
+ /**
+ * Width of the output of this operation.
+ */
+ unsigned int m_width;
+
+ /**
+ * Height of the output of this operation.
+ */
+ unsigned int m_height;
+
+ /**
+ * Flags how to evaluate this operation.
+ */
+ NodeOperationFlags flags;
public:
- virtual ~NodeOperation();
+ virtual ~NodeOperation()
+ {
+ }
+
+ void set_execution_model(const eExecutionModel model)
+ {
+ execution_model_ = model;
+ }
+
+ void set_name(const std::string name)
+ {
+ m_name = name;
+ }
+
+ const std::string get_name() const
+ {
+ return m_name;
+ }
+
+ void set_id(const int id)
+ {
+ m_id = id;
+ }
+
+ const int get_id() const
+ {
+ return m_id;
+ }
+
+ const NodeOperationFlags get_flags() const
+ {
+ return flags;
+ }
unsigned int getNumberOfInputSockets() const
{
@@ -130,19 +359,14 @@ class NodeOperation : public SocketReader {
{
return m_outputs.size();
}
- NodeOperationOutput *getOutputSocket(unsigned int index) const;
- NodeOperationOutput *getOutputSocket() const
- {
- return getOutputSocket(0);
- }
- NodeOperationInput *getInputSocket(unsigned int index) const;
+ NodeOperationOutput *getOutputSocket(unsigned int index = 0);
+ NodeOperationInput *getInputSocket(unsigned int index);
- /** Check if this is an input operation
- * An input operation is an operation that only has output sockets and no input sockets
- */
- bool isInputOperation() const
+ NodeOperation *get_input_operation(int index)
{
- return m_inputs.empty();
+ /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing
+ * this method. */
+ return getInputOperation(index);
}
/**
@@ -155,11 +379,11 @@ class NodeOperation : public SocketReader {
unsigned int preferredResolution[2]);
/**
- * \brief isOutputOperation determines whether this operation is an output of the ExecutionSystem
- * during rendering or editing.
+ * \brief isOutputOperation determines whether this operation is an output of the
+ * ExecutionSystem during rendering or editing.
*
- * Default behavior if not overridden, this operation will not be evaluated as being an output of
- * the ExecutionSystem.
+ * Default behavior if not overridden, this operation will not be evaluated as being an output
+ * of the ExecutionSystem.
*
* \see ExecutionSystem
* \ingroup check
@@ -174,11 +398,6 @@ class NodeOperation : public SocketReader {
return false;
}
- virtual int isSingleThreaded()
- {
- return false;
- }
-
void setbNodeTree(const bNodeTree *tree)
{
this->m_btree = tree;
@@ -241,62 +460,19 @@ class NodeOperation : public SocketReader {
}
virtual void deinitExecution();
- bool isResolutionSet()
- {
- return this->m_isResolutionSet;
- }
-
/**
* \brief set the resolution
* \param resolution: the resolution to set
*/
void setResolution(unsigned int resolution[2])
{
- if (!isResolutionSet()) {
+ if (!this->flags.is_resolution_set) {
this->m_width = resolution[0];
this->m_height = resolution[1];
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
}
- void getConnectedInputSockets(Inputs *sockets);
-
- /**
- * \brief is this operation complex
- *
- * Complex operations are typically doing many reads to calculate the output of a single pixel.
- * Mostly Filter types (Blurs, Convolution, Defocus etc) need this to be set to true.
- */
- bool isComplex() const
- {
- return this->m_complex;
- }
-
- virtual bool isSetOperation() const
- {
- return false;
- }
-
- /**
- * \brief is this operation of type ReadBufferOperation
- * \return [true:false]
- * \see ReadBufferOperation
- */
- virtual bool isReadBufferOperation() const
- {
- return false;
- }
-
- /**
- * \brief is this operation of type WriteBufferOperation
- * \return [true:false]
- * \see WriteBufferOperation
- */
- virtual bool isWriteBufferOperation() const
- {
- return false;
- }
-
/**
* \brief is this operation the active viewer output
* user can select an ViewerNode to be active
@@ -314,80 +490,123 @@ class NodeOperation : public SocketReader {
rcti *output);
/**
- * \brief set the index of the input socket that will determine the resolution of this operation
- * \param index: the index to set
+ * \brief set the index of the input socket that will determine the resolution of this
+ * operation \param index: the index to set
*/
void setResolutionInputSocketIndex(unsigned int index);
/**
* \brief get the render priority of this node.
* \note only applicable for output operations like ViewerOperation
- * \return CompositorPriority
+ * \return eCompositorPriority
*/
- virtual CompositorPriority getRenderPriority() const
+ virtual eCompositorPriority getRenderPriority() const
{
- return CompositorPriority::Low;
+ return eCompositorPriority::Low;
}
- /**
- * \brief can this NodeOperation be scheduled on an OpenCLDevice
- * \see WorkScheduler.schedule
- * \see ExecutionGroup.addOperation
- */
- bool isOpenCL() const
+ inline bool isBraked() const
{
- return this->m_openCL;
+ return this->m_btree->test_break(this->m_btree->tbh);
}
- virtual bool isViewerOperation() const
+ inline void updateDraw()
{
- return false;
+ if (this->m_btree->update_draw) {
+ this->m_btree->update_draw(this->m_btree->udh);
+ }
}
- virtual bool isPreviewOperation() const
+
+ unsigned int getWidth() const
{
- return false;
+ return m_width;
}
- virtual bool isFileOutputOperation() const
+
+ unsigned int getHeight() const
{
- return false;
+ return m_height;
}
- virtual bool isProxyOperation() const
+
+ inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
{
- return false;
+ executePixelSampled(result, x, y, sampler);
}
- virtual bool useDatatypeConversion() const
+ inline void readFiltered(float result[4], float x, float y, float dx[2], float dy[2])
{
- return true;
+ executePixelFiltered(result, x, y, dx, dy);
}
- inline bool isBraked() const
+ inline void read(float result[4], int x, int y, void *chunkData)
{
- return this->m_btree->test_break(this->m_btree->tbh);
+ executePixel(result, x, y, chunkData);
}
- inline void updateDraw()
+ virtual void *initializeTileData(rcti * /*rect*/)
+ {
+ return 0;
+ }
+
+ virtual void deinitializeTileData(rcti * /*rect*/, void * /*data*/)
{
- if (this->m_btree->update_draw) {
- this->m_btree->update_draw(this->m_btree->udh);
- }
}
+ virtual MemoryBuffer *getInputMemoryBuffer(MemoryBuffer ** /*memoryBuffers*/)
+ {
+ return 0;
+ }
+
+ /**
+ * Return the meta data associated with this branch.
+ *
+ * The return parameter holds an instance or is an nullptr. */
+ virtual std::unique_ptr<MetaData> getMetaData()
+ {
+ return std::unique_ptr<MetaData>();
+ }
+
+ /* -------------------------------------------------------------------- */
+ /** \name Full Frame Methods
+ * \{ */
+
+ void render(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system);
+
+ /**
+ * Executes operation updating output memory buffer. Single-threaded calls.
+ */
+ virtual void update_memory_buffer(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_area),
+ Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system))
+ {
+ }
+
+ /**
+ * Get input operation area being read by this operation on rendering given output area.
+ */
+ virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area);
+ void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area);
+
+ /** \} */
+
protected:
NodeOperation();
- void addInputSocket(DataType datatype, InputResizeMode resize_mode = COM_SC_CENTER);
+ void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center);
void addOutputSocket(DataType datatype);
void setWidth(unsigned int width)
{
this->m_width = width;
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
void setHeight(unsigned int height)
{
this->m_height = height;
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
SocketReader *getInputSocketReader(unsigned int inputSocketindex);
NodeOperation *getInputOperation(unsigned int inputSocketindex);
@@ -405,114 +624,83 @@ class NodeOperation : public SocketReader {
*/
void setComplex(bool complex)
{
- this->m_complex = complex;
+ this->flags.complex = complex;
}
/**
- * \brief set if this NodeOperation can be scheduled on a OpenCLDevice
+ * \brief calculate a single pixel
+ * \note this method is called for non-complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
*/
- void setOpenCL(bool openCL)
+ virtual void executePixelSampled(float /*output*/[4],
+ float /*x*/,
+ float /*y*/,
+ PixelSampler /*sampler*/)
{
- this->m_openCL = openCL;
}
- /* allow the DebugInfo class to look at internals */
- friend class DebugInfo;
-
-#ifdef WITH_CXX_GUARDEDALLOC
- MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
-#endif
-};
-
-class NodeOperationInput {
- private:
- NodeOperation *m_operation;
-
- /** Datatype of this socket. Is used for automatically data transformation.
- * \section data-conversion
+ /**
+ * \brief calculate a single pixel
+ * \note this method is called for complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
+ * \param chunkData: chunk specific data a during execution time.
*/
- DataType m_datatype;
-
- /** Resize mode of this socket */
- InputResizeMode m_resizeMode;
-
- /** Connected output */
- NodeOperationOutput *m_link;
-
- public:
- NodeOperationInput(NodeOperation *op,
- DataType datatype,
- InputResizeMode resizeMode = COM_SC_CENTER);
-
- NodeOperation &getOperation() const
- {
- return *m_operation;
- }
- DataType getDataType() const
- {
- return m_datatype;
- }
-
- void setLink(NodeOperationOutput *link)
+ virtual void executePixel(float output[4], int x, int y, void * /*chunkData*/)
{
- m_link = link;
- }
- NodeOperationOutput *getLink() const
- {
- return m_link;
- }
- bool isConnected() const
- {
- return m_link;
+ executePixelSampled(output, x, y, PixelSampler::Nearest);
}
- void setResizeMode(InputResizeMode resizeMode)
- {
- this->m_resizeMode = resizeMode;
- }
- InputResizeMode getResizeMode() const
+ /**
+ * \brief calculate a single pixel using an EWA filter
+ * \note this method is called for complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param dx:
+ * \param dy:
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
+ */
+ virtual void executePixelFiltered(
+ float /*output*/[4], float /*x*/, float /*y*/, float /*dx*/[2], float /*dy*/[2])
{
- return this->m_resizeMode;
}
- SocketReader *getReader();
+ private:
+ /* -------------------------------------------------------------------- */
+ /** \name Full Frame Methods
+ * \{ */
+
+ void render_full_frame(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system);
+
+ void render_full_frame_fallback(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system);
+ void render_tile(MemoryBuffer *output_buf, rcti *tile_rect);
+ Vector<NodeOperationOutput *> replace_inputs_with_buffers(Span<MemoryBuffer *> inputs_bufs);
+ void remove_buffers_and_restore_original_inputs(
+ Span<NodeOperationOutput *> original_inputs_links);
+
+ /** \} */
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+ /* allow the DebugInfo class to look at internals */
+ friend class DebugInfo;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
#endif
};
-class NodeOperationOutput {
- private:
- NodeOperation *m_operation;
-
- /** Datatype of this socket. Is used for automatically data transformation.
- * \section data-conversion
- */
- DataType m_datatype;
-
- public:
- NodeOperationOutput(NodeOperation *op, DataType datatype);
+std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operation_flags);
+std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation);
- NodeOperation &getOperation() const
- {
- return *m_operation;
- }
- DataType getDataType() const
- {
- return m_datatype;
- }
-
- /**
- * \brief determine the resolution of this data going through this socket
- * \param resolution: the result of this operation
- * \param preferredResolution: the preferable resolution as no resolution could be determined
- */
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
-
-#ifdef WITH_CXX_GUARDEDALLOC
- MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
-#endif
-};
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
index 1c741283c7b..1beb83bb477 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
@@ -38,24 +38,20 @@
#include "COM_NodeOperationBuilder.h" /* own include */
+namespace blender::compositor {
+
NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree)
: m_context(context), m_current_node(nullptr), m_active_viewer(nullptr)
{
m_graph.from_bNodeTree(*context, b_nodetree);
}
-NodeOperationBuilder::~NodeOperationBuilder()
-{
-}
-
void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
{
/* interface handle for nodes */
NodeConverter converter(this);
- for (int index = 0; index < m_graph.nodes().size(); index++) {
- Node *node = (Node *)m_graph.nodes()[index];
-
+ for (Node *node : m_graph.nodes()) {
m_current_node = node;
DebugInfo::node_to_operations(node);
@@ -69,7 +65,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
* so multiple operations can use the same node input.
*/
blender::MultiValueMap<NodeInput *, NodeOperationInput *> inverse_input_map;
- for (blender::Map<NodeOperationInput *, NodeInput *>::MutableItem item : m_input_map.items()) {
+ for (Map<NodeOperationInput *, NodeInput *>::MutableItem item : m_input_map.items()) {
inverse_input_map.add(item.value, item.key);
}
@@ -103,8 +99,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
determineResolutions();
- /* surround complex ops with read/write buffer */
- add_complex_operation_buffers();
+ if (m_context->get_execution_model() == eExecutionModel::Tiled) {
+ /* surround complex ops with read/write buffer */
+ add_complex_operation_buffers();
+ }
/* links not available from here on */
/* XXX make m_links a local variable to avoid confusion! */
@@ -115,8 +113,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
/* ensure topological (link-based) order of nodes */
/*sort_operations();*/ /* not needed yet */
- /* create execution groups */
- group_operations();
+ if (m_context->get_execution_model() == eExecutionModel::Tiled) {
+ /* create execution groups */
+ group_operations();
+ }
/* transfer resulting operations to the system */
system->set_operations(m_operations, m_groups);
@@ -124,7 +124,12 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
void NodeOperationBuilder::addOperation(NodeOperation *operation)
{
+ operation->set_id(m_operations.size());
m_operations.append(operation);
+ if (m_current_node) {
+ operation->set_name(m_current_node->getbNode()->name);
+ }
+ operation->set_execution_model(m_context->get_execution_model());
}
void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket,
@@ -251,12 +256,13 @@ void NodeOperationBuilder::registerViewer(ViewerOperation *viewer)
void NodeOperationBuilder::add_datatype_conversions()
{
- blender::Vector<Link> convert_links;
+ Vector<Link> convert_links;
for (const Link &link : m_links) {
/* proxy operations can skip data type conversion */
NodeOperation *from_op = &link.from()->getOperation();
NodeOperation *to_op = &link.to()->getOperation();
- if (!(from_op->useDatatypeConversion() || to_op->useDatatypeConversion())) {
+ if (!(from_op->get_flags().use_datatype_conversion ||
+ to_op->get_flags().use_datatype_conversion)) {
continue;
}
@@ -281,7 +287,7 @@ void NodeOperationBuilder::add_operation_input_constants()
/* Note: unconnected inputs cached first to avoid modifying
* m_operations while iterating over it
*/
- blender::Vector<NodeOperationInput *> pending_inputs;
+ Vector<NodeOperationInput *> pending_inputs;
for (NodeOperation *op : m_operations) {
for (int k = 0; k < op->getNumberOfInputSockets(); ++k) {
NodeOperationInput *input = op->getInputSocket(k);
@@ -349,11 +355,11 @@ void NodeOperationBuilder::add_input_constant_value(NodeOperationInput *input,
void NodeOperationBuilder::resolve_proxies()
{
- blender::Vector<Link> proxy_links;
+ Vector<Link> proxy_links;
for (const Link &link : m_links) {
/* don't replace links from proxy to proxy, since we may need them for replacing others! */
- if (link.from()->getOperation().isProxyOperation() &&
- !link.to()->getOperation().isProxyOperation()) {
+ if (link.from()->getOperation().get_flags().is_proxy_operation &&
+ !link.to()->getOperation().get_flags().is_proxy_operation) {
proxy_links.append(link);
}
}
@@ -364,7 +370,7 @@ void NodeOperationBuilder::resolve_proxies()
do {
/* walk upstream bypassing the proxy operation */
from = from->getOperation().getInputSocket(0)->getLink();
- } while (from && from->getOperation().isProxyOperation());
+ } while (from && from->getOperation().get_flags().is_proxy_operation);
removeInputLink(to);
/* we may not have a final proxy input link,
@@ -380,7 +386,7 @@ void NodeOperationBuilder::determineResolutions()
{
/* determine all resolutions of the operations (Width/Height) */
for (NodeOperation *op : m_operations) {
- if (op->isOutputOperation(m_context->isRendering()) && !op->isPreviewOperation()) {
+ if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) {
unsigned int resolution[2] = {0, 0};
unsigned int preferredResolution[2] = {0, 0};
op->determineResolution(resolution, preferredResolution);
@@ -389,7 +395,7 @@ void NodeOperationBuilder::determineResolutions()
}
for (NodeOperation *op : m_operations) {
- if (op->isOutputOperation(m_context->isRendering()) && op->isPreviewOperation()) {
+ if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) {
unsigned int resolution[2] = {0, 0};
unsigned int preferredResolution[2] = {0, 0};
op->determineResolution(resolution, preferredResolution);
@@ -399,9 +405,9 @@ void NodeOperationBuilder::determineResolutions()
/* add convert resolution operations when needed */
{
- blender::Vector<Link> convert_links;
+ Vector<Link> convert_links;
for (const Link &link : m_links) {
- if (link.to()->getResizeMode() != COM_SC_NO_RESIZE) {
+ if (link.to()->getResizeMode() != ResizeMode::None) {
NodeOperation &from_op = link.from()->getOperation();
NodeOperation &to_op = link.to()->getOperation();
if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) {
@@ -415,10 +421,10 @@ void NodeOperationBuilder::determineResolutions()
}
}
-blender::Vector<NodeOperationInput *> NodeOperationBuilder::cache_output_links(
+Vector<NodeOperationInput *> NodeOperationBuilder::cache_output_links(
NodeOperationOutput *output) const
{
- blender::Vector<NodeOperationInput *> inputs;
+ Vector<NodeOperationInput *> inputs;
for (const Link &link : m_links) {
if (link.from() == output) {
inputs.append(link.to());
@@ -433,7 +439,7 @@ WriteBufferOperation *NodeOperationBuilder::find_attached_write_buffer_operation
for (const Link &link : m_links) {
if (link.from() == output) {
NodeOperation &op = link.to()->getOperation();
- if (op.isWriteBufferOperation()) {
+ if (op.get_flags().is_write_buffer_operation) {
return (WriteBufferOperation *)(&op);
}
}
@@ -449,7 +455,7 @@ void NodeOperationBuilder::add_input_buffers(NodeOperation * /*operation*/,
}
NodeOperationOutput *output = input->getLink();
- if (output->getOperation().isReadBufferOperation()) {
+ if (output->getOperation().get_flags().is_read_buffer_operation) {
/* input is already buffered, no need to add another */
return;
}
@@ -483,7 +489,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation,
NodeOperationOutput *output)
{
/* cache connected sockets, so we can safely remove links first before replacing them */
- blender::Vector<NodeOperationInput *> targets = cache_output_links(output);
+ Vector<NodeOperationInput *> targets = cache_output_links(output);
if (targets.is_empty()) {
return;
}
@@ -491,7 +497,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation,
WriteBufferOperation *writeOperation = nullptr;
for (NodeOperationInput *target : targets) {
/* try to find existing write buffer operation */
- if (target->getOperation().isWriteBufferOperation()) {
+ if (target->getOperation().get_flags().is_write_buffer_operation) {
BLI_assert(writeOperation == nullptr); /* there should only be one write op connected */
writeOperation = (WriteBufferOperation *)(&target->getOperation());
}
@@ -534,9 +540,9 @@ void NodeOperationBuilder::add_complex_operation_buffers()
/* note: complex ops and get cached here first, since adding operations
* will invalidate iterators over the main m_operations
*/
- blender::Vector<NodeOperation *> complex_ops;
+ Vector<NodeOperation *> complex_ops;
for (NodeOperation *operation : m_operations) {
- if (operation->isComplex()) {
+ if (operation->get_flags().complex) {
complex_ops.append(operation);
}
}
@@ -571,7 +577,7 @@ static void find_reachable_operations_recursive(Tags &reachable, NodeOperation *
}
/* associated write-buffer operations are executed as well */
- if (op->isReadBufferOperation()) {
+ if (op->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read_op = (ReadBufferOperation *)op;
MemoryProxy *memproxy = read_op->getMemoryProxy();
find_reachable_operations_recursive(reachable, memproxy->getWriteBufferOperation());
@@ -589,7 +595,7 @@ void NodeOperationBuilder::prune_operations()
}
/* delete unreachable operations */
- blender::Vector<NodeOperation *> reachable_ops;
+ Vector<NodeOperation *> reachable_ops;
for (NodeOperation *op : m_operations) {
if (reachable.find(op) != reachable.end()) {
reachable_ops.append(op);
@@ -603,7 +609,7 @@ void NodeOperationBuilder::prune_operations()
}
/* topological (depth-first) sorting of operations */
-static void sort_operations_recursive(blender::Vector<NodeOperation *> &sorted,
+static void sort_operations_recursive(Vector<NodeOperation *> &sorted,
Tags &visited,
NodeOperation *op)
{
@@ -624,7 +630,7 @@ static void sort_operations_recursive(blender::Vector<NodeOperation *> &sorted,
void NodeOperationBuilder::sort_operations()
{
- blender::Vector<NodeOperation *> sorted;
+ Vector<NodeOperation *> sorted;
sorted.reserve(m_operations.size());
Tags visited;
@@ -657,7 +663,7 @@ static void add_group_operations_recursive(Tags &visited, NodeOperation *op, Exe
ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op)
{
- ExecutionGroup *group = new ExecutionGroup();
+ ExecutionGroup *group = new ExecutionGroup(this->m_groups.size());
m_groups.append(group);
Tags visited;
@@ -675,7 +681,7 @@ void NodeOperationBuilder::group_operations()
}
/* add new groups for associated memory proxies where needed */
- if (op->isReadBufferOperation()) {
+ if (op->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read_op = (ReadBufferOperation *)op;
MemoryProxy *memproxy = read_op->getMemoryProxy();
@@ -686,3 +692,42 @@ void NodeOperationBuilder::group_operations()
}
}
}
+
+/** Create a graphviz representation of the NodeOperationBuilder. */
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder)
+{
+ os << "# Builder start\n";
+ os << "digraph G {\n";
+ os << " rankdir=LR;\n";
+ os << " node [shape=box];\n";
+ for (const NodeOperation *operation : builder.get_operations()) {
+ os << " op" << operation->get_id() << " [label=\"" << *operation << "\"];\n";
+ }
+
+ os << "\n";
+ for (const NodeOperationBuilder::Link &link : builder.get_links()) {
+ os << " op" << link.from()->getOperation().get_id() << " -> op"
+ << link.to()->getOperation().get_id() << ";\n";
+ }
+ for (const NodeOperation *operation : builder.get_operations()) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ const ReadBufferOperation &read_operation = static_cast<const ReadBufferOperation &>(
+ *operation);
+ const WriteBufferOperation &write_operation =
+ *read_operation.getMemoryProxy()->getWriteBufferOperation();
+ os << " op" << write_operation.get_id() << " -> op" << read_operation.get_id() << ";\n";
+ }
+ }
+
+ os << "}\n";
+ os << "# Builder end\n";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link)
+{
+ os << link.from()->getOperation().get_id() << " -> " << link.to()->getOperation().get_id();
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h
index 8f55574fa88..b2fb822af25 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h
@@ -24,6 +24,8 @@
#include "COM_NodeGraph.h"
+namespace blender::compositor {
+
class CompositorContext;
class Node;
@@ -66,14 +68,14 @@ class NodeOperationBuilder {
const CompositorContext *m_context;
NodeGraph m_graph;
- blender::Vector<NodeOperation *> m_operations;
- blender::Vector<Link> m_links;
- blender::Vector<ExecutionGroup *> m_groups;
+ Vector<NodeOperation *> m_operations;
+ Vector<Link> m_links;
+ Vector<ExecutionGroup *> m_groups;
/** Maps operation inputs to node inputs */
- blender::Map<NodeOperationInput *, NodeInput *> m_input_map;
+ Map<NodeOperationInput *, NodeInput *> m_input_map;
/** Maps node outputs to operation outputs */
- blender::Map<NodeOutput *, NodeOperationOutput *> m_output_map;
+ Map<NodeOutput *, NodeOperationOutput *> m_output_map;
Node *m_current_node;
@@ -85,7 +87,6 @@ class NodeOperationBuilder {
public:
NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree);
- ~NodeOperationBuilder();
const CompositorContext &context() const
{
@@ -117,6 +118,16 @@ class NodeOperationBuilder {
return m_active_viewer;
}
+ const Vector<NodeOperation *> &get_operations() const
+ {
+ return m_operations;
+ }
+
+ const Vector<Link> &get_links() const
+ {
+ return m_links;
+ }
+
protected:
/** Add datatype conversion where needed */
void add_datatype_conversions();
@@ -132,7 +143,7 @@ class NodeOperationBuilder {
void determineResolutions();
/** Helper function to store connected inputs for replacement */
- blender::Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;
+ Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;
/** Find a connected write buffer operation to an OpOutput */
WriteBufferOperation *find_attached_write_buffer_operation(NodeOperationOutput *output) const;
/** Add read/write buffer operations around complex operations */
@@ -157,3 +168,8 @@ class NodeOperationBuilder {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompilerImpl")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder);
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc
index 4ac6bd50380..0f6ed0dbd2b 100644
--- a/source/blender/compositor/intern/COM_OpenCLDevice.cc
+++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc
@@ -19,6 +19,8 @@
#include "COM_OpenCLDevice.h"
#include "COM_WorkScheduler.h"
+namespace blender::compositor {
+
enum COM_VendorID { NVIDIA = 0x10DE, AMD = 0x1002 };
const cl_image_format IMAGE_FORMAT_COLOR = {
CL_RGBA,
@@ -48,6 +50,16 @@ OpenCLDevice::OpenCLDevice(cl_context context,
this->m_queue = clCreateCommandQueue(this->m_context, this->m_device, 0, &error);
}
+OpenCLDevice::OpenCLDevice(OpenCLDevice &&other) noexcept
+ : m_context(other.m_context),
+ m_device(other.m_device),
+ m_program(other.m_program),
+ m_queue(other.m_queue),
+ m_vendorID(other.m_vendorID)
+{
+ other.m_queue = nullptr;
+}
+
OpenCLDevice::~OpenCLDevice()
{
if (this->m_queue) {
@@ -55,18 +67,16 @@ OpenCLDevice::~OpenCLDevice()
}
}
-void OpenCLDevice::execute(WorkPackage *work)
+void OpenCLDevice::execute(WorkPackage *work_package)
{
- const unsigned int chunkNumber = work->chunk_number;
- ExecutionGroup *executionGroup = work->execution_group;
- rcti rect;
+ const unsigned int chunkNumber = work_package->chunk_number;
+ ExecutionGroup *executionGroup = work_package->execution_group;
- executionGroup->determineChunkRect(&rect, chunkNumber);
MemoryBuffer **inputBuffers = executionGroup->getInputBuffersOpenCL(chunkNumber);
- MemoryBuffer *outputBuffer = executionGroup->allocateOutputBuffer(rect);
+ MemoryBuffer *outputBuffer = executionGroup->allocateOutputBuffer(work_package->rect);
executionGroup->getOutputOperation()->executeOpenCLRegion(
- this, &rect, chunkNumber, inputBuffers, outputBuffer);
+ this, &work_package->rect, chunkNumber, inputBuffers, outputBuffer);
delete outputBuffer;
@@ -270,3 +280,5 @@ cl_kernel OpenCLDevice::COM_clCreateKernel(const char *kernelname,
}
return kernel;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h
index 30d9a59d182..826b0457a49 100644
--- a/source/blender/compositor/intern/COM_OpenCLDevice.h
+++ b/source/blender/compositor/intern/COM_OpenCLDevice.h
@@ -25,6 +25,8 @@ class OpenCLDevice;
#include "COM_WorkScheduler.h"
#include "clew.h"
+namespace blender::compositor {
+
/**
* \brief device representing an GPU OpenCL device.
* an instance of this class represents a single cl_device
@@ -65,6 +67,9 @@ class OpenCLDevice : public Device {
* \param vendorID:
*/
OpenCLDevice(cl_context context, cl_device_id device, cl_program program, cl_int vendorId);
+
+ OpenCLDevice(OpenCLDevice &&other) noexcept;
+
~OpenCLDevice();
/**
@@ -117,3 +122,5 @@ class OpenCLDevice : public Device {
NodeOperation *operation);
cl_kernel COM_clCreateKernel(const char *kernelname, std::list<cl_kernel> *clKernelsToCleanUp);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
new file mode 100644
index 00000000000..7e0486b0f54
--- /dev/null
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
@@ -0,0 +1,129 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_SharedOperationBuffers.h"
+#include "BLI_rect.h"
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+SharedOperationBuffers::BufferData::BufferData()
+ : buffer(nullptr), registered_reads(0), received_reads(0), is_rendered(false)
+{
+}
+
+SharedOperationBuffers::BufferData &SharedOperationBuffers::get_buffer_data(NodeOperation *op)
+{
+ return buffers_.lookup_or_add_cb(op, []() { return BufferData(); });
+}
+
+/**
+ * Whether given operation area to render is already registered.
+ * TODO: Possibly refactor to "request_area". Current implementation is incomplete: partial
+ * overlapping, etc. Leading to more rendering than necessary.
+ */
+bool SharedOperationBuffers::is_area_registered(NodeOperation *op, const rcti &area_to_render)
+{
+ BufferData &buf_data = get_buffer_data(op);
+ for (rcti &reg_rect : buf_data.render_areas) {
+ if (BLI_rcti_inside_rcti(&reg_rect, &area_to_render)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Registers an operation area to render.
+ */
+void SharedOperationBuffers::register_area(NodeOperation *op, const rcti &area_to_render)
+{
+ get_buffer_data(op).render_areas.append(area_to_render);
+}
+
+/**
+ * Whether given operation has any registered reads (other operation registered it depends on given
+ * operation).
+ */
+bool SharedOperationBuffers::has_registered_reads(NodeOperation *op)
+{
+ return get_buffer_data(op).registered_reads > 0;
+}
+
+/**
+ * Registers an operation read (other operation depends on given operation).
+ */
+void SharedOperationBuffers::register_read(NodeOperation *read_op)
+{
+ get_buffer_data(read_op).registered_reads++;
+}
+
+/**
+ * Get registered areas given operation needs to render.
+ */
+blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op)
+{
+ return get_buffer_data(op).render_areas.as_span();
+}
+
+/**
+ * Whether this operation buffer has already been rendered.
+ */
+bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op)
+{
+ return get_buffer_data(op).is_rendered;
+}
+
+/**
+ * Stores given operation rendered buffer.
+ */
+void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op,
+ std::unique_ptr<MemoryBuffer> buffer)
+{
+ BufferData &buf_data = get_buffer_data(op);
+ BLI_assert(buf_data.received_reads == 0);
+ BLI_assert(buf_data.buffer == nullptr);
+ buf_data.buffer = std::move(buffer);
+ buf_data.is_rendered = true;
+}
+
+/**
+ * Get given operation rendered buffer.
+ */
+MemoryBuffer *SharedOperationBuffers::get_rendered_buffer(NodeOperation *op)
+{
+ BLI_assert(is_operation_rendered(op));
+ return get_buffer_data(op).buffer.get();
+}
+
+/**
+ * Reports an operation has finished reading given operation. If all given operation dependencies
+ * have finished its buffer will be disposed.
+ */
+void SharedOperationBuffers::read_finished(NodeOperation *read_op)
+{
+ BufferData &buf_data = get_buffer_data(read_op);
+ buf_data.received_reads++;
+ BLI_assert(buf_data.received_reads > 0 && buf_data.received_reads <= buf_data.registered_reads);
+ if (buf_data.received_reads == buf_data.registered_reads) {
+ /* Dispose buffer. */
+ buf_data.buffer = nullptr;
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
new file mode 100644
index 00000000000..f7763cd8ae4
--- /dev/null
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
@@ -0,0 +1,71 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
+#include "COM_MemoryBuffer.h"
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+#include <memory>
+
+namespace blender::compositor {
+
+/**
+ * Stores and shares operations rendered buffers including render data. Buffers are
+ * disposed once all dependent operations have finished reading them.
+ */
+class SharedOperationBuffers {
+ private:
+ typedef struct BufferData {
+ public:
+ BufferData();
+ std::unique_ptr<MemoryBuffer> buffer;
+ blender::Vector<rcti> render_areas;
+ int registered_reads;
+ int received_reads;
+ bool is_rendered;
+ } BufferData;
+ blender::Map<NodeOperation *, BufferData> buffers_;
+
+ public:
+ bool is_area_registered(NodeOperation *op, const rcti &area_to_render);
+ void register_area(NodeOperation *op, const rcti &area_to_render);
+
+ bool has_registered_reads(NodeOperation *op);
+ void register_read(NodeOperation *read_op);
+
+ blender::Span<rcti> get_areas_to_render(NodeOperation *op);
+ bool is_operation_rendered(NodeOperation *op);
+ void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer);
+ MemoryBuffer *get_rendered_buffer(NodeOperation *op);
+
+ void read_finished(NodeOperation *read_op);
+
+ private:
+ BufferData &get_buffer_data(NodeOperation *op);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:SharedOperationBuffers")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
index 5febf3802de..01be6e1afed 100644
--- a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
+++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
@@ -18,10 +18,13 @@
#include "COM_SingleThreadedOperation.h"
+namespace blender::compositor {
+
SingleThreadedOperation::SingleThreadedOperation()
{
this->m_cachedInstance = nullptr;
- setComplex(true);
+ flags.complex = true;
+ flags.single_threaded = true;
}
void SingleThreadedOperation::initExecution()
@@ -56,3 +59,5 @@ void *SingleThreadedOperation::initializeTileData(rcti *rect)
unlockMutex();
return this->m_cachedInstance;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.h b/source/blender/compositor/intern/COM_SingleThreadedOperation.h
index 82326b57b57..9945f938ff9 100644
--- a/source/blender/compositor/intern/COM_SingleThreadedOperation.h
+++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SingleThreadedOperation : public NodeOperation {
private:
MemoryBuffer *m_cachedInstance;
@@ -51,9 +53,6 @@ class SingleThreadedOperation : public NodeOperation {
void *initializeTileData(rcti *rect) override;
virtual MemoryBuffer *createMemoryBuffer(rcti *rect) = 0;
-
- int isSingleThreaded() override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SocketReader.h b/source/blender/compositor/intern/COM_SocketReader.h
deleted file mode 100644
index 7c4132efe60..00000000000
--- a/source/blender/compositor/intern/COM_SocketReader.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2011, Blender Foundation.
- */
-
-#pragma once
-
-#include "BLI_rect.h"
-#include "COM_MetaData.h"
-#include "COM_defines.h"
-
-#include <memory>
-#include <optional>
-
-#ifdef WITH_CXX_GUARDEDALLOC
-# include "MEM_guardedalloc.h"
-#endif
-
-typedef enum PixelSampler {
- COM_PS_NEAREST = 0,
- COM_PS_BILINEAR = 1,
- COM_PS_BICUBIC = 2,
-} PixelSampler;
-
-class MemoryBuffer;
-
-/**
- * \brief Helper class for reading socket data.
- * Only use this class for dispatching (un-ary and n-ary) executions.
- * \ingroup Execution
- */
-class SocketReader {
- private:
- protected:
- /**
- * \brief Holds the width of the output of this operation.
- */
- unsigned int m_width;
-
- /**
- * \brief Holds the height of the output of this operation.
- */
- unsigned int m_height;
-
- /**
- * \brief calculate a single pixel
- * \note this method is called for non-complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- */
- virtual void executePixelSampled(float /*output*/[4],
- float /*x*/,
- float /*y*/,
- PixelSampler /*sampler*/)
- {
- }
-
- /**
- * \brief calculate a single pixel
- * \note this method is called for complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- * \param chunkData: chunk specific data a during execution time.
- */
- virtual void executePixel(float output[4], int x, int y, void * /*chunkData*/)
- {
- executePixelSampled(output, x, y, COM_PS_NEAREST);
- }
-
- /**
- * \brief calculate a single pixel using an EWA filter
- * \note this method is called for complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param dx:
- * \param dy:
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- */
- virtual void executePixelFiltered(
- float /*output*/[4], float /*x*/, float /*y*/, float /*dx*/[2], float /*dy*/[2])
- {
- }
-
- public:
- inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
- {
- executePixelSampled(result, x, y, sampler);
- }
- inline void read(float result[4], int x, int y, void *chunkData)
- {
- executePixel(result, x, y, chunkData);
- }
- inline void readFiltered(float result[4], float x, float y, float dx[2], float dy[2])
- {
- executePixelFiltered(result, x, y, dx, dy);
- }
-
- virtual void *initializeTileData(rcti * /*rect*/)
- {
- return 0;
- }
- virtual void deinitializeTileData(rcti * /*rect*/, void * /*data*/)
- {
- }
-
- virtual ~SocketReader()
- {
- }
-
- virtual MemoryBuffer *getInputMemoryBuffer(MemoryBuffer ** /*memoryBuffers*/)
- {
- return 0;
- }
-
- inline unsigned int getWidth() const
- {
- return this->m_width;
- }
- inline unsigned int getHeight() const
- {
- return this->m_height;
- }
-
- /* Return the meta data associated with this branch.
- *
- * The return parameter holds an instance or is an nullptr. */
- virtual std::unique_ptr<MetaData> getMetaData() const
- {
- return std::unique_ptr<MetaData>();
- }
-
-#ifdef WITH_CXX_GUARDEDALLOC
- MEM_CXX_CLASS_ALLOC_FUNCS("COM:SocketReader")
-#endif
-};
diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc
new file mode 100644
index 00000000000..d025ce53330
--- /dev/null
+++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_TiledExecutionModel.h"
+#include "COM_Debug.h"
+#include "COM_ExecutionGroup.h"
+#include "COM_ReadBufferOperation.h"
+#include "COM_WorkScheduler.h"
+
+#include "BLT_translation.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+TiledExecutionModel::TiledExecutionModel(CompositorContext &context,
+ Span<NodeOperation *> operations,
+ Span<ExecutionGroup *> groups)
+ : ExecutionModel(context, operations), groups_(groups)
+{
+ const bNodeTree *node_tree = context.getbNodeTree();
+ node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution"));
+
+ unsigned int resolution[2];
+ for (ExecutionGroup *group : groups_) {
+ resolution[0] = 0;
+ resolution[1] = 0;
+ group->determineResolution(resolution);
+
+ if (border_.use_render_border) {
+ const rctf *render_border = border_.viewer_border;
+ group->setRenderBorder(
+ render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax);
+ }
+
+ if (border_.use_viewer_border) {
+ const rctf *viewer_border = border_.viewer_border;
+ group->setViewerBorder(
+ viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax);
+ }
+ }
+}
+
+static void update_read_buffer_offset(Span<NodeOperation *> operations)
+{
+ unsigned int order = 0;
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
+ readOperation->setOffset(order);
+ order++;
+ }
+ }
+}
+
+static void init_write_operations_for_execution(Span<NodeOperation *> operations,
+ const bNodeTree *bTree)
+{
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_write_buffer_operation) {
+ operation->setbNodeTree(bTree);
+ operation->initExecution();
+ }
+ }
+}
+
+static void link_write_buffers(Span<NodeOperation *> operations)
+{
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
+ readOperation->updateMemoryBuffer();
+ }
+ }
+}
+
+static void init_non_write_operations_for_execution(Span<NodeOperation *> operations,
+ const bNodeTree *bTree)
+{
+ for (NodeOperation *operation : operations) {
+ if (!operation->get_flags().is_write_buffer_operation) {
+ operation->setbNodeTree(bTree);
+ operation->initExecution();
+ }
+ }
+}
+
+static void init_execution_groups_for_execution(Span<ExecutionGroup *> groups,
+ const int chunk_size)
+{
+ for (ExecutionGroup *execution_group : groups) {
+ execution_group->setChunksize(chunk_size);
+ execution_group->initExecution();
+ }
+}
+
+void TiledExecutionModel::execute(ExecutionSystem &exec_system)
+{
+ const bNodeTree *editingtree = this->context_.getbNodeTree();
+
+ editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution"));
+
+ update_read_buffer_offset(operations_);
+
+ init_write_operations_for_execution(operations_, context_.getbNodeTree());
+ link_write_buffers(operations_);
+ init_non_write_operations_for_execution(operations_, context_.getbNodeTree());
+ init_execution_groups_for_execution(groups_, context_.getChunksize());
+
+ WorkScheduler::start(context_);
+ execute_groups(eCompositorPriority::High, exec_system);
+ if (!context_.isFastCalculation()) {
+ execute_groups(eCompositorPriority::Medium, exec_system);
+ execute_groups(eCompositorPriority::Low, exec_system);
+ }
+ WorkScheduler::finish();
+ WorkScheduler::stop();
+
+ editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution"));
+
+ for (NodeOperation *operation : operations_) {
+ operation->deinitExecution();
+ }
+
+ for (ExecutionGroup *execution_group : groups_) {
+ execution_group->deinitExecution();
+ }
+}
+
+void TiledExecutionModel::execute_groups(eCompositorPriority priority,
+ ExecutionSystem &exec_system)
+{
+ for (ExecutionGroup *execution_group : groups_) {
+ if (execution_group->get_flags().is_output &&
+ execution_group->getRenderPriority() == priority) {
+ execution_group->execute(&exec_system);
+ }
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h
new file mode 100644
index 00000000000..05a795b9f07
--- /dev/null
+++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h
@@ -0,0 +1,54 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_ExecutionModel.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+class ExecutionGroup;
+
+/**
+ * Operations are executed from outputs to inputs grouped in execution groups and rendered in
+ * tiles.
+ */
+class TiledExecutionModel : public ExecutionModel {
+ private:
+ Span<ExecutionGroup *> groups_;
+
+ public:
+ TiledExecutionModel(CompositorContext &context,
+ Span<NodeOperation *> operations,
+ Span<ExecutionGroup *> groups);
+
+ void execute(ExecutionSystem &exec_system) override;
+
+ private:
+ void execute_groups(eCompositorPriority priority, ExecutionSystem &exec_system);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:TiledExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkPackage.cc b/source/blender/compositor/intern/COM_WorkPackage.cc
index 60684f2c45c..ea78c0d6333 100644
--- a/source/blender/compositor/intern/COM_WorkPackage.cc
+++ b/source/blender/compositor/intern/COM_WorkPackage.cc
@@ -18,8 +18,20 @@
#include "COM_WorkPackage.h"
-WorkPackage::WorkPackage(ExecutionGroup *execution_group, unsigned int chunk_number)
+#include "COM_Enums.h"
+#include "COM_ExecutionGroup.h"
+
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const WorkPackage &work_package)
{
- this->execution_group = execution_group;
- this->chunk_number = chunk_number;
+ os << "WorkPackage(execution_group=" << *work_package.execution_group;
+ os << ",chunk=" << work_package.chunk_number;
+ os << ",state=" << work_package.state;
+ os << ",rect=(" << work_package.rect.xmin << "," << work_package.rect.ymin << ")-("
+ << work_package.rect.xmax << "," << work_package.rect.ymax << ")";
+ os << ")";
+ return os;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h
index db5eb3d3072..f0f53f300a5 100644
--- a/source/blender/compositor/intern/COM_WorkPackage.h
+++ b/source/blender/compositor/intern/COM_WorkPackage.h
@@ -18,14 +18,30 @@
#pragma once
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+#include "COM_Enums.h"
+
+#include "BLI_rect.h"
+
+#include <functional>
+#include <ostream>
+
+namespace blender::compositor {
+// Forward Declarations.
class ExecutionGroup;
-#include "COM_ExecutionGroup.h"
/**
* \brief contains data about work that can be scheduled
* \see WorkScheduler
*/
struct WorkPackage {
+ eWorkPackageType type;
+
+ eWorkPackageState state = eWorkPackageState::NotScheduled;
+
/**
* \brief executionGroup with the operations-setup to be evaluated
*/
@@ -37,13 +53,25 @@ struct WorkPackage {
unsigned int chunk_number;
/**
- * constructor
- * \param group: the ExecutionGroup
- * \param chunk_number: the number of the chunk
+ * Area of the execution group that the work package calculates.
*/
- WorkPackage(ExecutionGroup *group, unsigned int chunk_number);
+ rcti rect;
+
+ /**
+ * Custom function to execute when work package type is CustomFunction.
+ */
+ std::function<void()> execute_fn;
+
+ /**
+ * Called when work execution is finished.
+ */
+ std::function<void()> executed_fn;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkPackage")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const WorkPackage &work_package);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc
index 7d9dd502762..cd0139fd18e 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.cc
+++ b/source/blender/compositor/intern/COM_WorkScheduler.cc
@@ -37,6 +37,8 @@
#include "BKE_global.h"
+namespace blender::compositor {
+
enum class ThreadingModel {
/** Everything is executed in the caller thread. easy for debugging. */
SingleThreaded,
@@ -70,7 +72,7 @@ static struct {
/** \brief list of all CPUDevices. for every hardware thread an instance of CPUDevice is
* created
*/
- blender::Vector<CPUDevice> devices;
+ Vector<CPUDevice> devices;
/** \brief list of all thread for every CPUDevice in cpudevices a thread exists. */
ListBase threads;
@@ -89,13 +91,15 @@ static struct {
cl_program program;
/** \brief list of all OpenCLDevices. for every OpenCL GPU device an instance of OpenCLDevice
* is created. */
- blender::Vector<OpenCLDevice> devices;
+ Vector<OpenCLDevice> devices;
/** \brief list of all thread for every GPUDevice in cpudevices a thread exists. */
ListBase threads;
/** \brief all scheduled work for the GPU. */
bool active = false;
bool initialized = false;
} opencl;
+
+ int num_cpu_threads;
} g_work_scheduler;
/* -------------------------------------------------------------------- */
@@ -117,7 +121,6 @@ static void *thread_execute_gpu(void *data)
while ((work = (WorkPackage *)BLI_thread_queue_pop(g_work_scheduler.opencl.queue))) {
device->execute(work);
- delete work;
}
return nullptr;
@@ -142,7 +145,8 @@ static void opencl_start(CompositorContext &context)
static bool opencl_schedule(WorkPackage *package)
{
- if (package->execution_group->isOpenCL() && g_work_scheduler.opencl.active) {
+ if (package->type == eWorkPackageType::Tile && package->execution_group->get_flags().open_cl &&
+ g_work_scheduler.opencl.active) {
BLI_thread_queue_push(g_work_scheduler.opencl.queue, package);
return true;
}
@@ -262,10 +266,10 @@ static void opencl_initialize(const bool use_opencl)
if (error2 != CL_SUCCESS) {
printf("CLERROR[%d]: %s\n", error2, clewErrorString(error2));
}
- g_work_scheduler.opencl.devices.append(OpenCLDevice(g_work_scheduler.opencl.context,
- device,
- g_work_scheduler.opencl.program,
- vendorID));
+ g_work_scheduler.opencl.devices.append_as(g_work_scheduler.opencl.context,
+ device,
+ g_work_scheduler.opencl.program,
+ vendorID);
}
}
MEM_freeN(cldevices);
@@ -304,7 +308,6 @@ static void threading_model_single_thread_execute(WorkPackage *package)
{
CPUDevice device(0);
device.execute(package);
- delete package;
}
/* \} */
@@ -320,7 +323,6 @@ static void *threading_model_queue_execute(void *data)
BLI_thread_local_set(g_thread_device, device);
while ((work = (WorkPackage *)BLI_thread_queue_pop(g_work_scheduler.queue.queue))) {
device->execute(work);
- delete work;
}
return nullptr;
@@ -369,7 +371,7 @@ static void threading_model_queue_initialize(const int num_cpu_threads)
/* Initialize CPU threads. */
if (!g_work_scheduler.queue.initialized) {
for (int index = 0; index < num_cpu_threads; index++) {
- g_work_scheduler.queue.devices.append(CPUDevice(index));
+ g_work_scheduler.queue.devices.append_as(index);
}
BLI_thread_local_create(g_thread_device);
g_work_scheduler.queue.initialized = true;
@@ -398,7 +400,6 @@ static void threading_model_task_execute(TaskPool *__restrict UNUSED(pool), void
CPUDevice device(BLI_task_parallel_thread_id(nullptr));
BLI_thread_local_set(g_thread_device, &device);
device.execute(package);
- delete package;
}
static void threading_model_task_schedule(WorkPackage *package)
@@ -410,7 +411,8 @@ static void threading_model_task_schedule(WorkPackage *package)
static void threading_model_task_start()
{
BLI_thread_local_create(g_thread_device);
- g_work_scheduler.task.pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH);
+ g_work_scheduler.task.pool = BLI_task_pool_create(
+ nullptr, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
static void threading_model_task_finish()
@@ -431,10 +433,8 @@ static void threading_model_task_stop()
/** \name Public API
* \{ */
-void WorkScheduler::schedule(ExecutionGroup *group, int chunkNumber)
+void WorkScheduler::schedule(WorkPackage *package)
{
- WorkPackage *package = new WorkPackage(group, chunkNumber);
-
if (COM_is_opencl_enabled()) {
if (opencl_schedule(package)) {
return;
@@ -536,11 +536,12 @@ void WorkScheduler::initialize(bool use_opencl, int num_cpu_threads)
opencl_initialize(use_opencl);
}
+ g_work_scheduler.num_cpu_threads = num_cpu_threads;
switch (COM_threading_model()) {
case ThreadingModel::SingleThreaded:
+ g_work_scheduler.num_cpu_threads = 1;
/* Nothing to do. */
break;
-
case ThreadingModel::Queue:
threading_model_queue_initialize(num_cpu_threads);
break;
@@ -572,6 +573,11 @@ void WorkScheduler::deinitialize()
}
}
+int WorkScheduler::get_num_cpu_threads()
+{
+ return g_work_scheduler.num_cpu_threads;
+}
+
int WorkScheduler::current_thread_id()
{
if (COM_threading_model() == ThreadingModel::SingleThreaded) {
@@ -583,3 +589,5 @@ int WorkScheduler::current_thread_id()
}
/* \} */
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h
index 6b53cc3efd6..be88859be7c 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.h
+++ b/source/blender/compositor/intern/COM_WorkScheduler.h
@@ -24,6 +24,8 @@
#include "COM_WorkPackage.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
/** \brief the workscheduler
* \ingroup execution
*/
@@ -31,13 +33,11 @@ struct WorkScheduler {
/**
* \brief schedule a chunk of a group to be calculated.
* An execution group schedules a chunk in the WorkScheduler
- * when ExecutionGroup.isOpenCL is set the work will be handled by a OpenCLDevice
+ * when ExecutionGroup.get_flags().open_cl is set the work will be handled by a OpenCLDevice
* otherwise the work is scheduled for an CPUDevice
* \see ExecutionGroup.execute
- * \param group: the execution group
- * \param chunkNumber: the number of the chunk in the group to be executed
*/
- static void schedule(ExecutionGroup *group, int chunkNumber);
+ static void schedule(WorkPackage *package);
/**
* \brief initialize the WorkScheduler
@@ -87,9 +87,13 @@ struct WorkScheduler {
*/
static bool has_gpu_devices();
+ static int get_num_cpu_threads();
+
static int current_thread_id();
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkScheduler")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc
index 68e4f80f91f..5839f53976b 100644
--- a/source/blender/compositor/intern/COM_compositor.cc
+++ b/source/blender/compositor/intern/COM_compositor.cc
@@ -46,12 +46,12 @@ static void compositor_init_node_previews(const RenderData *render_data, bNodeTr
1.0f;
int preview_width, preview_height;
if (aspect < 1.0f) {
- preview_width = COM_PREVIEW_SIZE;
- preview_height = (int)(COM_PREVIEW_SIZE * aspect);
+ preview_width = blender::compositor::COM_PREVIEW_SIZE;
+ preview_height = (int)(blender::compositor::COM_PREVIEW_SIZE * aspect);
}
else {
- preview_width = (int)(COM_PREVIEW_SIZE / aspect);
- preview_height = COM_PREVIEW_SIZE;
+ preview_width = (int)(blender::compositor::COM_PREVIEW_SIZE / aspect);
+ preview_height = blender::compositor::COM_PREVIEW_SIZE;
}
BKE_node_preview_init_tree(node_tree, preview_width, preview_height, false);
}
@@ -92,12 +92,12 @@ void COM_execute(RenderData *render_data,
/* Initialize workscheduler. */
const bool use_opencl = (node_tree->flag & NTREE_COM_OPENCL) != 0;
- WorkScheduler::initialize(use_opencl, BKE_render_num_threads(render_data));
+ blender::compositor::WorkScheduler::initialize(use_opencl, BKE_render_num_threads(render_data));
/* Execute. */
const bool twopass = (node_tree->flag & NTREE_TWO_PASS) && !rendering;
if (twopass) {
- ExecutionSystem fast_pass(
+ blender::compositor::ExecutionSystem fast_pass(
render_data, scene, node_tree, rendering, true, viewSettings, displaySettings, viewName);
fast_pass.execute();
@@ -107,7 +107,7 @@ void COM_execute(RenderData *render_data,
}
}
- ExecutionSystem system(
+ blender::compositor::ExecutionSystem system(
render_data, scene, node_tree, rendering, false, viewSettings, displaySettings, viewName);
system.execute();
@@ -118,7 +118,7 @@ void COM_deinitialize()
{
if (g_compositor.is_initialized) {
BLI_mutex_lock(&g_compositor.mutex);
- WorkScheduler::deinitialize();
+ blender::compositor::WorkScheduler::deinitialize();
g_compositor.is_initialized = false;
BLI_mutex_unlock(&g_compositor.mutex);
BLI_mutex_end(&g_compositor.mutex);
diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc
index 640fbf49808..5e09902aee2 100644
--- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc
+++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc
@@ -26,6 +26,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_material_types.h" /* the ramp types */
+namespace blender::compositor {
+
void AlphaOverNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -64,3 +66,5 @@ void AlphaOverNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(2), convertProg->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(0), convertProg->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.h b/source/blender/compositor/nodes/COM_AlphaOverNode.h
index cbfda2885d7..201c8ed5b6e 100644
--- a/source/blender/compositor/nodes/COM_AlphaOverNode.h
+++ b/source/blender/compositor/nodes/COM_AlphaOverNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief AlphaOverNode
* \ingroup Node
@@ -32,3 +34,5 @@ class AlphaOverNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
new file mode 100644
index 00000000000..af4832665df
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ *
+ * Contributor: IRIE Shinsuke
+ */
+
+#include "COM_AntiAliasingNode.h"
+#include "COM_SMAAOperation.h"
+#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
+void AntiAliasingNode::convertToOperations(NodeConverter &converter,
+ const CompositorContext & /*context*/) const
+{
+ bNode *node = this->getbNode();
+ NodeAntiAliasingData *data = (NodeAntiAliasingData *)node->storage;
+
+ /* Edge Detection (First Pass) */
+ SMAAEdgeDetectionOperation *operation1 = nullptr;
+
+ operation1 = new SMAAEdgeDetectionOperation();
+ operation1->setThreshold(data->threshold);
+ operation1->setLocalContrastAdaptationFactor(data->contrast_limit);
+ converter.addOperation(operation1);
+
+ converter.mapInputSocket(getInputSocket(0), operation1->getInputSocket(0));
+
+ /* Blending Weight Calculation Pixel Shader (Second Pass) */
+ SMAABlendingWeightCalculationOperation *operation2 =
+ new SMAABlendingWeightCalculationOperation();
+ operation2->setCornerRounding(data->corner_rounding);
+ converter.addOperation(operation2);
+
+ converter.addLink(operation1->getOutputSocket(), operation2->getInputSocket(0));
+
+ /* Neighborhood Blending Pixel Shader (Third Pass) */
+ SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation();
+ converter.addOperation(operation3);
+
+ converter.mapInputSocket(getInputSocket(0), operation3->getInputSocket(0));
+ converter.addLink(operation2->getOutputSocket(), operation3->getInputSocket(1));
+ converter.mapOutputSocket(getOutputSocket(0), operation3->getOutputSocket());
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.h b/source/blender/compositor/nodes/COM_AntiAliasingNode.h
new file mode 100644
index 00000000000..d4a6d0d26dc
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ *
+ * Contributor: IRIE Shinsuke
+ */
+
+#pragma once
+
+#include "COM_Node.h"
+
+namespace blender::compositor {
+
+/**
+ * @brief AntiAliasingNode
+ * @ingroup Node
+ */
+class AntiAliasingNode : public Node {
+ public:
+ AntiAliasingNode(bNode *editorNode) : Node(editorNode)
+ {
+ }
+ void convertToOperations(NodeConverter &converter,
+ const CompositorContext &context) const override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc
index e8037f923f2..1b9da789d3c 100644
--- a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
BilateralBlurNode::BilateralBlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -39,3 +41,5 @@ void BilateralBlurNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.h b/source/blender/compositor/nodes/COM_BilateralBlurNode.h
index 39abc47bfaa..fed2612ac02 100644
--- a/source/blender/compositor/nodes/COM_BilateralBlurNode.h
+++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BilateralBlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BilateralBlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BlurNode.cc b/source/blender/compositor/nodes/COM_BlurNode.cc
index b82bede8443..c10bc2a05f0 100644
--- a/source/blender/compositor/nodes/COM_BlurNode.cc
+++ b/source/blender/compositor/nodes/COM_BlurNode.cc
@@ -29,6 +29,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
BlurNode::BlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,7 +47,7 @@ void BlurNode::convertToOperations(NodeConverter &converter,
const float size = this->getInputSocket(1)->getEditorValueFloat();
const bool extend_bounds = (editorNode->custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS) != 0;
- CompositorQuality quality = context.getQuality();
+ eCompositorQuality quality = context.getQuality();
NodeOperation *input_operation = nullptr, *output_operation = nullptr;
if (data->filtertype == R_FILTER_FAST_GAUSS) {
@@ -168,3 +170,5 @@ void BlurNode::convertToOperations(NodeConverter &converter,
converter.addPreview(output_operation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BlurNode.h b/source/blender/compositor/nodes/COM_BlurNode.h
index 6f756445ea2..61cdc17f3a9 100644
--- a/source/blender/compositor/nodes/COM_BlurNode.h
+++ b/source/blender/compositor/nodes/COM_BlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.cc b/source/blender/compositor/nodes/COM_BokehBlurNode.cc
index 5096dbef608..1d2a0dae390 100644
--- a/source/blender/compositor/nodes/COM_BokehBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_BokehBlurNode.cc
@@ -25,6 +25,8 @@
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+namespace blender::compositor {
+
BokehBlurNode::BokehBlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -75,3 +77,5 @@ void BokehBlurNode::convertToOperations(NodeConverter &converter,
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.h b/source/blender/compositor/nodes/COM_BokehBlurNode.h
index 3d73fc50dbc..2c060936025 100644
--- a/source/blender/compositor/nodes/COM_BokehBlurNode.h
+++ b/source/blender/compositor/nodes/COM_BokehBlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BokehBlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BokehBlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.cc b/source/blender/compositor/nodes/COM_BokehImageNode.cc
index 87fe4979c1d..2b0a47c76bc 100644
--- a/source/blender/compositor/nodes/COM_BokehImageNode.cc
+++ b/source/blender/compositor/nodes/COM_BokehImageNode.cc
@@ -20,6 +20,8 @@
#include "COM_BokehImageOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
BokehImageNode::BokehImageNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -36,3 +38,5 @@ void BokehImageNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.h b/source/blender/compositor/nodes/COM_BokehImageNode.h
index 0adb671ddc3..323561a7e4f 100644
--- a/source/blender/compositor/nodes/COM_BokehImageNode.h
+++ b/source/blender/compositor/nodes/COM_BokehImageNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BokehImageNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BokehImageNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
index fe59bd32939..14f42cc42f7 100644
--- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
@@ -23,6 +23,8 @@
#include "COM_ScaleOperation.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
BoxMaskNode::BoxMaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -60,7 +62,7 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
- scaleOperation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
@@ -70,3 +72,5 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.h b/source/blender/compositor/nodes/COM_BoxMaskNode.h
index 572228d2684..46cedf7af75 100644
--- a/source/blender/compositor/nodes/COM_BoxMaskNode.h
+++ b/source/blender/compositor/nodes/COM_BoxMaskNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BoxMaskNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BoxMaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.cc b/source/blender/compositor/nodes/COM_BrightnessNode.cc
index fcd2a6de1f4..b64f1fea99f 100644
--- a/source/blender/compositor/nodes/COM_BrightnessNode.cc
+++ b/source/blender/compositor/nodes/COM_BrightnessNode.cc
@@ -20,6 +20,8 @@
#include "COM_BrightnessOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
BrightnessNode::BrightnessNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void BrightnessNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.h b/source/blender/compositor/nodes/COM_BrightnessNode.h
index 4ae50e23798..1084108b1c3 100644
--- a/source/blender/compositor/nodes/COM_BrightnessNode.h
+++ b/source/blender/compositor/nodes/COM_BrightnessNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BrightnessNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BrightnessNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
index 598cd7b7745..d0f72274aea 100644
--- a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
@@ -22,6 +22,8 @@
#include "COM_ConvertOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
ChannelMatteNode::ChannelMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -93,3 +95,5 @@ void ChannelMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operationAlpha->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.h b/source/blender/compositor/nodes/COM_ChannelMatteNode.h
index f282ae81afc..46100b3f7ea 100644
--- a/source/blender/compositor/nodes/COM_ChannelMatteNode.h
+++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ChannelMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ChannelMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
index 83e88b35f92..9abf183a843 100644
--- a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
@@ -22,6 +22,8 @@
#include "COM_ConvertOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
ChromaMatteNode::ChromaMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -63,3 +65,5 @@ void ChromaMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operationAlpha->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.h b/source/blender/compositor/nodes/COM_ChromaMatteNode.h
index 196c69bfe9d..f3ddd013fa4 100644
--- a/source/blender/compositor/nodes/COM_ChromaMatteNode.h
+++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ChromaMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ChromaMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
index 596d9631297..03e4e143061 100644
--- a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
@@ -23,6 +23,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
ColorBalanceNode::ColorBalanceNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -71,3 +73,5 @@ void ColorBalanceNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputImageSocket, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.h b/source/blender/compositor/nodes/COM_ColorBalanceNode.h
index 0391fbe25c3..243713b4912 100644
--- a/source/blender/compositor/nodes/COM_ColorBalanceNode.h
+++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorBalanceNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorBalanceNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
index 92b334fddb9..6397b1d8e4b 100644
--- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
@@ -20,6 +20,8 @@
#include "COM_ColorCorrectionOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
ColorCorrectionNode::ColorCorrectionNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -41,3 +43,5 @@ void ColorCorrectionNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.h b/source/blender/compositor/nodes/COM_ColorCorrectionNode.h
index 35878dde32f..aee07ee07a3 100644
--- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.h
+++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorCorrectionNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorCorrectionNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.cc b/source/blender/compositor/nodes/COM_ColorCurveNode.cc
index e1888f3f0bc..774dd689a46 100644
--- a/source/blender/compositor/nodes/COM_ColorCurveNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorCurveNode.cc
@@ -20,6 +20,8 @@
#include "COM_ColorCurveOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
ColorCurveNode::ColorCurveNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -55,3 +57,5 @@ void ColorCurveNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.h b/source/blender/compositor/nodes/COM_ColorCurveNode.h
index 18806c00264..89786b47cf5 100644
--- a/source/blender/compositor/nodes/COM_ColorCurveNode.h
+++ b/source/blender/compositor/nodes/COM_ColorCurveNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorCurveNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorCurveNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.cc b/source/blender/compositor/nodes/COM_ColorExposureNode.cc
index cd0285ac373..a8f164e6b66 100644
--- a/source/blender/compositor/nodes/COM_ColorExposureNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorExposureNode.cc
@@ -20,6 +20,8 @@
#include "COM_ColorExposureOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
ExposureNode::ExposureNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -35,3 +37,5 @@ void ExposureNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.h b/source/blender/compositor/nodes/COM_ColorExposureNode.h
index 97fa9c97650..df9bfc65f81 100644
--- a/source/blender/compositor/nodes/COM_ColorExposureNode.h
+++ b/source/blender/compositor/nodes/COM_ColorExposureNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ExposureNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ExposureNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.cc b/source/blender/compositor/nodes/COM_ColorMatteNode.cc
index 865eee5427f..eadb8ce4f96 100644
--- a/source/blender/compositor/nodes/COM_ColorMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorMatteNode.cc
@@ -22,6 +22,8 @@
#include "COM_ConvertOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
ColorMatteNode::ColorMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -61,3 +63,5 @@ void ColorMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operationAlpha->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.h b/source/blender/compositor/nodes/COM_ColorMatteNode.h
index 3f47d707d38..9d70b6d8416 100644
--- a/source/blender/compositor/nodes/COM_ColorMatteNode.h
+++ b/source/blender/compositor/nodes/COM_ColorMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorNode.cc b/source/blender/compositor/nodes/COM_ColorNode.cc
index e6f8bfa01fe..f8277645a4b 100644
--- a/source/blender/compositor/nodes/COM_ColorNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_SetColorOperation.h"
+namespace blender::compositor {
+
ColorNode::ColorNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -37,3 +39,5 @@ void ColorNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(output, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorNode.h b/source/blender/compositor/nodes/COM_ColorNode.h
index 5df60dfbe1c..ae3bf575bb4 100644
--- a/source/blender/compositor/nodes/COM_ColorNode.h
+++ b/source/blender/compositor/nodes/COM_ColorNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.cc b/source/blender/compositor/nodes/COM_ColorRampNode.cc
index 1504a76cee7..6b44ef3057e 100644
--- a/source/blender/compositor/nodes/COM_ColorRampNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorRampNode.cc
@@ -23,6 +23,8 @@
#include "COM_ExecutionSystem.h"
#include "DNA_texture_types.h"
+namespace blender::compositor {
+
ColorRampNode::ColorRampNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -50,3 +52,5 @@ void ColorRampNode::convertToOperations(NodeConverter &converter,
converter.addLink(operation->getOutputSocket(), operation2->getInputSocket(0));
converter.mapOutputSocket(outputSocketAlpha, operation2->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.h b/source/blender/compositor/nodes/COM_ColorRampNode.h
index 81c3a2f78dc..d0c0e43d56c 100644
--- a/source/blender/compositor/nodes/COM_ColorRampNode.h
+++ b/source/blender/compositor/nodes/COM_ColorRampNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorRampNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorRampNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.cc b/source/blender/compositor/nodes/COM_ColorSpillNode.cc
index d1a3099e998..119cff93e84 100644
--- a/source/blender/compositor/nodes/COM_ColorSpillNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorSpillNode.cc
@@ -20,6 +20,8 @@
#include "BKE_node.h"
#include "COM_ColorSpillOperation.h"
+namespace blender::compositor {
+
ColorSpillNode::ColorSpillNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,3 +47,5 @@ void ColorSpillNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocketFac, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocketImage, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.h b/source/blender/compositor/nodes/COM_ColorSpillNode.h
index 3026f335ba6..731a76e8811 100644
--- a/source/blender/compositor/nodes/COM_ColorSpillNode.h
+++ b/source/blender/compositor/nodes/COM_ColorSpillNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorSpillNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorSpillNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.cc b/source/blender/compositor/nodes/COM_ColorToBWNode.cc
index 4115bad5d3f..dcedfc19e4d 100644
--- a/source/blender/compositor/nodes/COM_ColorToBWNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorToBWNode.cc
@@ -21,6 +21,8 @@
#include "COM_ConvertOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
ColorToBWNode::ColorToBWNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void ColorToBWNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(colorSocket, convertProg->getInputSocket(0));
converter.mapOutputSocket(valueSocket, convertProg->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.h b/source/blender/compositor/nodes/COM_ColorToBWNode.h
index 475563f0830..60c08a3c886 100644
--- a/source/blender/compositor/nodes/COM_ColorToBWNode.h
+++ b/source/blender/compositor/nodes/COM_ColorToBWNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief ColorToBWNode
* \ingroup Node
@@ -30,3 +33,5 @@ class ColorToBWNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc
index 12968f06a10..8a2bbba1c1e 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNode.cc
+++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc
@@ -20,6 +20,8 @@
#include "COM_ConvertOperation.h"
+namespace blender::compositor {
+
CombineColorNode::CombineColorNode(bNode *editorNode) : Node(editorNode)
{
}
@@ -87,3 +89,5 @@ NodeOperation *CombineYUVANode::getColorConverter(const CompositorContext & /*co
{
return new ConvertYUVToRGBOperation();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.h b/source/blender/compositor/nodes/COM_CombineColorNode.h
index e6a8f5acfb7..29d3fa37817 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNode.h
+++ b/source/blender/compositor/nodes/COM_CombineColorNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
class CombineColorNode : public Node {
public:
CombineColorNode(bNode *editorNode);
@@ -65,3 +67,5 @@ class CombineYUVANode : public CombineColorNode {
NodeOperation *getColorConverter(const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cc b/source/blender/compositor/nodes/COM_CompositorNode.cc
index 32ac1fccec9..262fa550915 100644
--- a/source/blender/compositor/nodes/COM_CompositorNode.cc
+++ b/source/blender/compositor/nodes/COM_CompositorNode.cc
@@ -20,6 +20,8 @@
#include "COM_CompositorOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
CompositorNode::CompositorNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -59,3 +61,5 @@ void CompositorNode::convertToOperations(NodeConverter &converter,
converter.addNodeInputPreview(imageSocket);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CompositorNode.h b/source/blender/compositor/nodes/COM_CompositorNode.h
index 488cfe88d24..4da9f9a766f 100644
--- a/source/blender/compositor/nodes/COM_CompositorNode.h
+++ b/source/blender/compositor/nodes/COM_CompositorNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief CompositorNode
* \ingroup Node
@@ -30,3 +33,5 @@ class CompositorNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
index 2921b44c95b..ac4e45357dc 100644
--- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
+++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
@@ -20,6 +20,8 @@
#include "COM_ConvertOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
void ConvertAlphaNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -39,3 +41,5 @@ void ConvertAlphaNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.h b/source/blender/compositor/nodes/COM_ConvertAlphaNode.h
index 45be8925ad2..f3d0ef2cd5b 100644
--- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.h
+++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ConvertAlphaNode
* \ingroup Node
@@ -32,3 +34,5 @@ class ConvertAlphaNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.cc b/source/blender/compositor/nodes/COM_CornerPinNode.cc
index efe847bbfbf..6a120cffe0a 100644
--- a/source/blender/compositor/nodes/COM_CornerPinNode.cc
+++ b/source/blender/compositor/nodes/COM_CornerPinNode.cc
@@ -20,6 +20,8 @@
#include "COM_PlaneCornerPinOperation.h"
+namespace blender::compositor {
+
CornerPinNode::CornerPinNode(bNode *editorNode) : Node(editorNode)
{
}
@@ -53,3 +55,5 @@ void CornerPinNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(output_warped_image, warp_image_operation->getOutputSocket());
converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.h b/source/blender/compositor/nodes/COM_CornerPinNode.h
index 5cbedcb7057..779e057ebb5 100644
--- a/source/blender/compositor/nodes/COM_CornerPinNode.h
+++ b/source/blender/compositor/nodes/COM_CornerPinNode.h
@@ -21,6 +21,8 @@
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief CornerPinNode
* \ingroup Node
@@ -31,3 +33,5 @@ class CornerPinNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CropNode.cc b/source/blender/compositor/nodes/COM_CropNode.cc
index 0f0883b0151..3f01062c789 100644
--- a/source/blender/compositor/nodes/COM_CropNode.cc
+++ b/source/blender/compositor/nodes/COM_CropNode.cc
@@ -19,6 +19,8 @@
#include "COM_CropNode.h"
#include "COM_CropOperation.h"
+namespace blender::compositor {
+
CropNode::CropNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,3 +47,5 @@ void CropNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CropNode.h b/source/blender/compositor/nodes/COM_CropNode.h
index 54ada91a5cc..be3c9a268f9 100644
--- a/source/blender/compositor/nodes/COM_CropNode.h
+++ b/source/blender/compositor/nodes/COM_CropNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief CropNode
* \ingroup Node
@@ -30,3 +32,5 @@ class CropNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
index 591db8a96e5..4032a655633 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
@@ -31,6 +31,8 @@
#include <iterator>
#include <string>
+namespace blender::compositor {
+
/** \name Cryptomatte base
* \{ */
@@ -75,10 +77,10 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter,
/** \name Cryptomatte V2
* \{ */
-static std::string prefix_from_node(const bNode &node)
+static std::string prefix_from_node(const CompositorContext &context, const bNode &node)
{
char prefix[MAX_NAME];
- ntreeCompositCryptomatteLayerPrefix(&node, prefix, sizeof(prefix));
+ ntreeCompositCryptomatteLayerPrefix(context.getScene(), &node, prefix, sizeof(prefix));
return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix)));
}
@@ -101,7 +103,7 @@ static std::string combined_layer_pass_name(RenderLayer *render_layer, RenderPas
void CryptomatteNode::input_operations_from_render_source(
const CompositorContext &context,
const bNode &node,
- blender::Vector<NodeOperation *> &r_input_operations)
+ Vector<NodeOperation *> &r_input_operations)
{
Scene *scene = (Scene *)node.id;
if (!scene) {
@@ -116,9 +118,9 @@ void CryptomatteNode::input_operations_from_render_source(
return;
}
- const short cryptomatte_layer_id = 0;
- const std::string prefix = prefix_from_node(node);
- LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ short view_layer_id = 0;
+ const std::string prefix = prefix_from_node(context, node);
+ LISTBASE_FOREACH_INDEX (ViewLayer *, view_layer, &scene->view_layers, view_layer_id) {
RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name);
if (render_layer) {
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
@@ -127,7 +129,7 @@ void CryptomatteNode::input_operations_from_render_source(
RenderLayersProg *op = new RenderLayersProg(
render_pass->name, DataType::Color, render_pass->channels);
op->setScene(scene);
- op->setLayerId(cryptomatte_layer_id);
+ op->setLayerId(view_layer_id);
op->setRenderData(context.getRenderData());
op->setViewName(context.getViewName());
r_input_operations.append(op);
@@ -141,7 +143,7 @@ void CryptomatteNode::input_operations_from_render_source(
void CryptomatteNode::input_operations_from_image_source(
const CompositorContext &context,
const bNode &node,
- blender::Vector<NodeOperation *> &r_input_operations)
+ Vector<NodeOperation *> &r_input_operations)
{
NodeCryptomatte *cryptomatte_settings = (NodeCryptomatte *)node.storage;
Image *image = (Image *)node.id;
@@ -175,8 +177,13 @@ void CryptomatteNode::input_operations_from_image_source(
}
}
- const std::string prefix = prefix_from_node(node);
- LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) {
+ const std::string prefix = prefix_from_node(context, node);
+ int layer_index;
+ LISTBASE_FOREACH_INDEX (RenderLayer *, render_layer, &image->rr->layers, layer_index) {
+ if (!blender::StringRef(prefix).startswith(blender::StringRef(
+ render_layer->name, BLI_strnlen(render_layer->name, sizeof(render_layer->name))))) {
+ continue;
+ }
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
if (blender::StringRef(combined_name).startswith(prefix)) {
@@ -184,19 +191,21 @@ void CryptomatteNode::input_operations_from_image_source(
render_layer, render_pass, view);
op->setImage(image);
op->setImageUser(iuser);
+ iuser->layer = layer_index;
op->setFramenumber(context.getFramenumber());
r_input_operations.append(op);
}
}
+ break;
}
}
BKE_image_release_ibuf(image, ibuf, nullptr);
}
-blender::Vector<NodeOperation *> CryptomatteNode::create_input_operations(
- const CompositorContext &context, const bNode &node)
+Vector<NodeOperation *> CryptomatteNode::create_input_operations(const CompositorContext &context,
+ const bNode &node)
{
- blender::Vector<NodeOperation *> input_operations;
+ Vector<NodeOperation *> input_operations;
switch (node.custom1) {
case CMP_CRYPTOMATTE_SRC_RENDER:
input_operations_from_render_source(context, node, input_operations);
@@ -222,7 +231,7 @@ CryptomatteOperation *CryptomatteNode::create_cryptomatte_operation(
const bNode &node,
const NodeCryptomatte *cryptomatte_settings) const
{
- blender::Vector<NodeOperation *> input_operations = create_input_operations(context, node);
+ Vector<NodeOperation *> input_operations = create_input_operations(context, node);
CryptomatteOperation *operation = new CryptomatteOperation(input_operations.size());
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) {
operation->addObjectIndex(cryptomatte_entry->encoded_hash);
@@ -245,7 +254,7 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation(
const bNode &UNUSED(node),
const NodeCryptomatte *cryptomatte_settings) const
{
- const int num_inputs = getNumberOfInputSockets() - 1;
+ const int num_inputs = inputs.size() - 1;
CryptomatteOperation *operation = new CryptomatteOperation(num_inputs);
if (cryptomatte_settings) {
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) {
@@ -261,3 +270,5 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation(
}
/* \} */
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.h b/source/blender/compositor/nodes/COM_CryptomatteNode.h
index 0ea05eb50f7..eacb49e2033 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.h
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.h
@@ -24,6 +24,8 @@
#include "COM_CryptomatteOperation.h"
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief CryptomatteNode
* \ingroup Node
@@ -62,16 +64,14 @@ class CryptomatteNode : public CryptomatteBaseNode {
const NodeCryptomatte *cryptomatte_settings) const override;
private:
- static blender::Vector<NodeOperation *> create_input_operations(const CompositorContext &context,
- const bNode &node);
- static void input_operations_from_render_source(
- const CompositorContext &context,
- const bNode &node,
- blender::Vector<NodeOperation *> &r_input_operations);
- static void input_operations_from_image_source(
- const CompositorContext &context,
- const bNode &node,
- blender::Vector<NodeOperation *> &r_input_operations);
+ static Vector<NodeOperation *> create_input_operations(const CompositorContext &context,
+ const bNode &node);
+ static void input_operations_from_render_source(const CompositorContext &context,
+ const bNode &node,
+ Vector<NodeOperation *> &r_input_operations);
+ static void input_operations_from_image_source(const CompositorContext &context,
+ const bNode &node,
+ Vector<NodeOperation *> &r_input_operations);
};
class CryptomatteLegacyNode : public CryptomatteBaseNode {
@@ -88,3 +88,5 @@ class CryptomatteLegacyNode : public CryptomatteBaseNode {
const bNode &node,
const NodeCryptomatte *cryptomatte_settings) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DefocusNode.cc b/source/blender/compositor/nodes/COM_DefocusNode.cc
index 2343b14f68d..2023e4f7118 100644
--- a/source/blender/compositor/nodes/COM_DefocusNode.cc
+++ b/source/blender/compositor/nodes/COM_DefocusNode.cc
@@ -30,6 +30,8 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+namespace blender::compositor {
+
DefocusNode::DefocusNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -110,7 +112,7 @@ void DefocusNode::convertToOperations(NodeConverter &converter,
VariableSizeBokehBlurOperation *operation = new VariableSizeBokehBlurOperation();
if (data->preview) {
- operation->setQuality(CompositorQuality::Low);
+ operation->setQuality(eCompositorQuality::Low);
}
else {
operation->setQuality(context.getQuality());
@@ -141,3 +143,5 @@ void DefocusNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DefocusNode.h b/source/blender/compositor/nodes/COM_DefocusNode.h
index 447950c66dd..5e51a0ccd52 100644
--- a/source/blender/compositor/nodes/COM_DefocusNode.h
+++ b/source/blender/compositor/nodes/COM_DefocusNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DefocusNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DefocusNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cc b/source/blender/compositor/nodes/COM_DenoiseNode.cc
index 1aae81e1e7b..e58a9c7ba9a 100644
--- a/source/blender/compositor/nodes/COM_DenoiseNode.cc
+++ b/source/blender/compositor/nodes/COM_DenoiseNode.cc
@@ -21,6 +21,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
DenoiseNode::DenoiseNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -41,3 +43,5 @@ void DenoiseNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.h b/source/blender/compositor/nodes/COM_DenoiseNode.h
index eb0857fe514..91be8e3e3ad 100644
--- a/source/blender/compositor/nodes/COM_DenoiseNode.h
+++ b/source/blender/compositor/nodes/COM_DenoiseNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DenoiseNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DenoiseNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.cc b/source/blender/compositor/nodes/COM_DespeckleNode.cc
index 58734917831..beda479025d 100644
--- a/source/blender/compositor/nodes/COM_DespeckleNode.cc
+++ b/source/blender/compositor/nodes/COM_DespeckleNode.cc
@@ -22,6 +22,8 @@
#include "COM_ExecutionSystem.h"
#include "DNA_scene_types.h"
+namespace blender::compositor {
+
DespeckleNode::DespeckleNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -46,3 +48,5 @@ void DespeckleNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.h b/source/blender/compositor/nodes/COM_DespeckleNode.h
index 47879bc3d3c..2f268e99e1b 100644
--- a/source/blender/compositor/nodes/COM_DespeckleNode.h
+++ b/source/blender/compositor/nodes/COM_DespeckleNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DespeckleNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DespeckleNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
index 3d538e9b4a0..8c989bfc04e 100644
--- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
@@ -21,6 +21,8 @@
#include "COM_DifferenceMatteOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
DifferenceMatteNode::DifferenceMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -52,3 +54,5 @@ void DifferenceMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.h b/source/blender/compositor/nodes/COM_DifferenceMatteNode.h
index db149a02c07..a173c723192 100644
--- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.h
+++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DifferenceMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DifferenceMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cc b/source/blender/compositor/nodes/COM_DilateErodeNode.cc
index e90707618e5..1867014f64b 100644
--- a/source/blender/compositor/nodes/COM_DilateErodeNode.cc
+++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cc
@@ -24,6 +24,8 @@
#include "COM_GaussianAlphaXBlurOperation.h"
#include "COM_GaussianAlphaYBlurOperation.h"
+namespace blender::compositor {
+
DilateErodeNode::DilateErodeNode(bNode *editorNode) : Node(editorNode)
{
/* initialize node data */
@@ -83,7 +85,7 @@ void DilateErodeNode::convertToOperations(NodeConverter &converter,
}
else if (editorNode->custom1 == CMP_NODE_DILATEERODE_DISTANCE_FEATHER) {
/* this uses a modified gaussian blur function otherwise its far too slow */
- CompositorQuality quality = context.getQuality();
+ eCompositorQuality quality = context.getQuality();
GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation();
operationx->setData(&m_alpha_blur);
@@ -147,3 +149,5 @@ void DilateErodeNode::convertToOperations(NodeConverter &converter,
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.h b/source/blender/compositor/nodes/COM_DilateErodeNode.h
index 78cc2d21a7a..7684d7e3834 100644
--- a/source/blender/compositor/nodes/COM_DilateErodeNode.h
+++ b/source/blender/compositor/nodes/COM_DilateErodeNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DilateErodeNode
* \ingroup Node
@@ -33,3 +35,5 @@ class DilateErodeNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
index f8d0eaf4675..90c4236bce8 100644
--- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
DirectionalBlurNode::DirectionalBlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void DirectionalBlurNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.h b/source/blender/compositor/nodes/COM_DirectionalBlurNode.h
index 2f64559e378..ce3ef378aaf 100644
--- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.h
+++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DirectionalBlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DirectionalBlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.cc b/source/blender/compositor/nodes/COM_DisplaceNode.cc
index 5b63bc1f393..f2ec750c595 100644
--- a/source/blender/compositor/nodes/COM_DisplaceNode.cc
+++ b/source/blender/compositor/nodes/COM_DisplaceNode.cc
@@ -21,6 +21,8 @@
#include "COM_DisplaceSimpleOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
DisplaceNode::DisplaceNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -30,7 +32,7 @@ void DisplaceNode::convertToOperations(NodeConverter &converter,
const CompositorContext &context) const
{
NodeOperation *operation;
- if (context.getQuality() == CompositorQuality::Low) {
+ if (context.getQuality() == eCompositorQuality::Low) {
operation = new DisplaceSimpleOperation();
}
else {
@@ -44,3 +46,5 @@ void DisplaceNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(3));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.h b/source/blender/compositor/nodes/COM_DisplaceNode.h
index 5496d6833c1..b2495839da3 100644
--- a/source/blender/compositor/nodes/COM_DisplaceNode.h
+++ b/source/blender/compositor/nodes/COM_DisplaceNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DisplaceNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DisplaceNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
index 37aeb5c8504..4450c4a2f4a 100644
--- a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
@@ -23,6 +23,8 @@
#include "COM_DistanceYCCMatteOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
DistanceMatteNode::DistanceMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -96,3 +98,5 @@ void DistanceMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operationAlpha->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.h b/source/blender/compositor/nodes/COM_DistanceMatteNode.h
index c237edc4d2d..0baa531b4d2 100644
--- a/source/blender/compositor/nodes/COM_DistanceMatteNode.h
+++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DistanceMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DistanceMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
index 907a9f49353..847dcc2f8f1 100644
--- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
@@ -20,6 +20,8 @@
#include "COM_DoubleEdgeMaskOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
DoubleEdgeMaskNode::DoubleEdgeMaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -40,3 +42,5 @@ void DoubleEdgeMaskNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h
index 0244ba2cbe2..90e009747c1 100644
--- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h
+++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DoubleEdgeMaskNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DoubleEdgeMaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
index 1ae855c0f1d..3b4f5ca8c94 100644
--- a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
@@ -23,6 +23,8 @@
#include "COM_ScaleOperation.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
EllipseMaskNode::EllipseMaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -60,7 +62,7 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
- scaleOperation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
@@ -70,3 +72,5 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.h b/source/blender/compositor/nodes/COM_EllipseMaskNode.h
index 87f2c498165..cbe189be9f6 100644
--- a/source/blender/compositor/nodes/COM_EllipseMaskNode.h
+++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief EllipseMaskNode
* \ingroup Node
@@ -30,3 +32,5 @@ class EllipseMaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_FilterNode.cc b/source/blender/compositor/nodes/COM_FilterNode.cc
index 1147c11794f..351219155c2 100644
--- a/source/blender/compositor/nodes/COM_FilterNode.cc
+++ b/source/blender/compositor/nodes/COM_FilterNode.cc
@@ -23,6 +23,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
FilterNode::FilterNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -94,3 +96,5 @@ void FilterNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_FilterNode.h b/source/blender/compositor/nodes/COM_FilterNode.h
index c86e65b5807..f7f4176cea5 100644
--- a/source/blender/compositor/nodes/COM_FilterNode.h
+++ b/source/blender/compositor/nodes/COM_FilterNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief FilterNode
* \ingroup Node
@@ -30,3 +32,5 @@ class FilterNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_FlipNode.cc b/source/blender/compositor/nodes/COM_FlipNode.cc
index d89e6b7b844..bca6cd3c4f7 100644
--- a/source/blender/compositor/nodes/COM_FlipNode.cc
+++ b/source/blender/compositor/nodes/COM_FlipNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_FlipOperation.h"
+namespace blender::compositor {
+
FlipNode::FlipNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -52,3 +54,5 @@ void FlipNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_FlipNode.h b/source/blender/compositor/nodes/COM_FlipNode.h
index 29fa904c4f6..ee61d09fbba 100644
--- a/source/blender/compositor/nodes/COM_FlipNode.h
+++ b/source/blender/compositor/nodes/COM_FlipNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief FlipNode
* \ingroup Node
@@ -30,3 +32,5 @@ class FlipNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_GammaNode.cc b/source/blender/compositor/nodes/COM_GammaNode.cc
index 1ce17faa0dc..52148a80a8f 100644
--- a/source/blender/compositor/nodes/COM_GammaNode.cc
+++ b/source/blender/compositor/nodes/COM_GammaNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_GammaOperation.h"
+namespace blender::compositor {
+
GammaNode::GammaNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -35,3 +37,5 @@ void GammaNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_GammaNode.h b/source/blender/compositor/nodes/COM_GammaNode.h
index b3bf9b649b9..29c9ed170fa 100644
--- a/source/blender/compositor/nodes/COM_GammaNode.h
+++ b/source/blender/compositor/nodes/COM_GammaNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief GammaNode
* \ingroup Node
@@ -30,3 +32,5 @@ class GammaNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc
index ef088e42205..0537074552a 100644
--- a/source/blender/compositor/nodes/COM_GlareNode.cc
+++ b/source/blender/compositor/nodes/COM_GlareNode.cc
@@ -27,6 +27,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
GlareNode::GlareNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -61,11 +63,11 @@ void GlareNode::convertToOperations(NodeConverter &converter,
thresholdOperation->setGlareSettings(glare);
SetValueOperation *mixvalueoperation = new SetValueOperation();
- mixvalueoperation->setValue(0.5f + glare->mix * 0.5f);
+ mixvalueoperation->setValue(glare->mix);
MixGlareOperation *mixoperation = new MixGlareOperation();
mixoperation->setResolutionInputSocketIndex(1);
- mixoperation->getInputSocket(2)->setResizeMode(COM_SC_FIT);
+ mixoperation->getInputSocket(2)->setResizeMode(ResizeMode::FitAny);
converter.addOperation(glareoperation);
converter.addOperation(thresholdOperation);
@@ -80,3 +82,5 @@ void GlareNode::convertToOperations(NodeConverter &converter,
converter.addLink(glareoperation->getOutputSocket(), mixoperation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(), mixoperation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_GlareNode.h b/source/blender/compositor/nodes/COM_GlareNode.h
index dbc02656006..7db5fa85e04 100644
--- a/source/blender/compositor/nodes/COM_GlareNode.h
+++ b/source/blender/compositor/nodes/COM_GlareNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief GlareNode
* \ingroup Node
@@ -30,3 +32,5 @@ class GlareNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
index 00125ba2ea5..5042d217f9a 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
@@ -26,6 +26,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
HueSaturationValueCorrectNode::HueSaturationValueCorrectNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -62,3 +64,5 @@ void HueSaturationValueCorrectNode::convertToOperations(
converter.mapInputSocket(valueSocket, blend->getInputSocket(0));
converter.mapOutputSocket(outputSocket, blend->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h
index e7656d2296c..d75b2ba51ca 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief HueSaturationValueCorrectNode
* \ingroup Node
@@ -30,3 +32,5 @@ class HueSaturationValueCorrectNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
index dc2e5187e8f..54d2caa75af 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
@@ -26,6 +26,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
HueSaturationValueNode::HueSaturationValueNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -65,3 +67,5 @@ void HueSaturationValueNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(facSocket, blend->getInputSocket(0));
converter.mapOutputSocket(outputSocket, blend->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.h b/source/blender/compositor/nodes/COM_HueSaturationValueNode.h
index b35f6ae14b7..0b295158cc7 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.h
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief HueSaturationValueNode
* \ingroup Node
@@ -30,3 +32,5 @@ class HueSaturationValueNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc
index 5ba54d75bcd..9798dabd035 100644
--- a/source/blender/compositor/nodes/COM_IDMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_IDMaskOperation.h"
+namespace blender::compositor {
+
IDMaskNode::IDMaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -47,3 +49,5 @@ void IDMaskNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), antiAliasOperation->getOutputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.h b/source/blender/compositor/nodes/COM_IDMaskNode.h
index 2766377e47a..f702732a8ed 100644
--- a/source/blender/compositor/nodes/COM_IDMaskNode.h
+++ b/source/blender/compositor/nodes/COM_IDMaskNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief IDMaskNode
* \ingroup Node
@@ -30,3 +32,5 @@ class IDMaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc
index 711399ccd63..f0bfda0f40e 100644
--- a/source/blender/compositor/nodes/COM_ImageNode.cc
+++ b/source/blender/compositor/nodes/COM_ImageNode.cc
@@ -29,6 +29,8 @@
#include "COM_SetValueOperation.h"
#include "COM_SetVectorOperation.h"
+namespace blender::compositor {
+
ImageNode::ImageNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -77,7 +79,6 @@ void ImageNode::convertToOperations(NodeConverter &converter,
Image *image = (Image *)editorNode->id;
ImageUser *imageuser = (ImageUser *)editorNode->storage;
int framenumber = context.getFramenumber();
- int numberOfOutputs = this->getNumberOfOutputSockets();
bool outputStraightAlpha = (editorNode->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT) != 0;
BKE_image_user_frame_calc(image, imageuser, context.getFramenumber());
/* force a load, we assume iuser index will be set OK anyway */
@@ -87,14 +88,11 @@ void ImageNode::convertToOperations(NodeConverter &converter,
if (image->rr) {
RenderLayer *rl = (RenderLayer *)BLI_findlink(&image->rr->layers, imageuser->layer);
if (rl) {
- NodeOutput *socket;
- int index;
-
is_multilayer_ok = true;
- for (index = 0; index < numberOfOutputs; index++) {
+ for (int64_t index = 0; index < outputs.size(); index++) {
+ NodeOutput *socket = outputs[index];
NodeOperation *operation = nullptr;
- socket = this->getOutputSocket(index);
bNodeSocket *bnodeSocket = socket->getbNodeSocket();
NodeImageLayer *storage = (NodeImageLayer *)bnodeSocket->storage;
RenderPass *rpass = (RenderPass *)BLI_findstring(
@@ -171,8 +169,7 @@ void ImageNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket());
}
if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
- for (int alphaIndex = 0; alphaIndex < numberOfOutputs; alphaIndex++) {
- NodeOutput *alphaSocket = this->getOutputSocket(alphaIndex);
+ for (NodeOutput *alphaSocket : getOutputSockets()) {
bNodeSocket *bnodeAlphaSocket = alphaSocket->getbNodeSocket();
if (!STREQ(bnodeAlphaSocket->name, "Alpha")) {
continue;
@@ -204,12 +201,13 @@ void ImageNode::convertToOperations(NodeConverter &converter,
/* without this, multilayer that fail to load will crash blender T32490. */
if (is_multilayer_ok == false) {
- for (int i = 0; i < getNumberOfOutputSockets(); i++) {
- converter.setInvalidOutput(getOutputSocket(i));
+ for (NodeOutput *output : getOutputSockets()) {
+ converter.setInvalidOutput(output);
}
}
}
else {
+ const int64_t numberOfOutputs = getOutputSockets().size();
if (numberOfOutputs > 0) {
ImageOperation *operation = new ImageOperation();
operation->setImage(image);
@@ -296,4 +294,6 @@ void ImageNode::convertToOperations(NodeConverter &converter,
}
}
}
-}
+} // namespace blender::compositor
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ImageNode.h b/source/blender/compositor/nodes/COM_ImageNode.h
index 716687c91b4..047cc496f83 100644
--- a/source/blender/compositor/nodes/COM_ImageNode.h
+++ b/source/blender/compositor/nodes/COM_ImageNode.h
@@ -26,6 +26,8 @@
#include "RE_engine.h"
#include "RE_pipeline.h"
+namespace blender::compositor {
+
/**
* \brief ImageNode
* \ingroup Node
@@ -47,3 +49,5 @@ class ImageNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_InpaintNode.cc b/source/blender/compositor/nodes/COM_InpaintNode.cc
index 40fe63ec9f3..01ec5523939 100644
--- a/source/blender/compositor/nodes/COM_InpaintNode.cc
+++ b/source/blender/compositor/nodes/COM_InpaintNode.cc
@@ -22,6 +22,8 @@
#include "COM_InpaintOperation.h"
#include "DNA_scene_types.h"
+namespace blender::compositor {
+
InpaintNode::InpaintNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -43,3 +45,5 @@ void InpaintNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_InpaintNode.h b/source/blender/compositor/nodes/COM_InpaintNode.h
index 864d8bbd5cd..3a10c11bf61 100644
--- a/source/blender/compositor/nodes/COM_InpaintNode.h
+++ b/source/blender/compositor/nodes/COM_InpaintNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief InpaintNode
* \ingroup Node
@@ -30,3 +32,5 @@ class InpaintNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_InvertNode.cc b/source/blender/compositor/nodes/COM_InvertNode.cc
index 913452c42c8..5fe2033227f 100644
--- a/source/blender/compositor/nodes/COM_InvertNode.cc
+++ b/source/blender/compositor/nodes/COM_InvertNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_InvertOperation.h"
+namespace blender::compositor {
+
InvertNode::InvertNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -39,3 +41,5 @@ void InvertNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_InvertNode.h b/source/blender/compositor/nodes/COM_InvertNode.h
index ab5f80a5e87..1cc975b8236 100644
--- a/source/blender/compositor/nodes/COM_InvertNode.h
+++ b/source/blender/compositor/nodes/COM_InvertNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief InvertNode
* \ingroup Node
@@ -30,3 +32,5 @@ class InvertNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cc b/source/blender/compositor/nodes/COM_KeyingNode.cc
index 9b493d3f332..0af328a3601 100644
--- a/source/blender/compositor/nodes/COM_KeyingNode.cc
+++ b/source/blender/compositor/nodes/COM_KeyingNode.cc
@@ -37,6 +37,8 @@
#include "COM_GaussianAlphaXBlurOperation.h"
#include "COM_GaussianAlphaYBlurOperation.h"
+namespace blender::compositor {
+
KeyingNode::KeyingNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -142,7 +144,7 @@ NodeOperationOutput *KeyingNode::setupFeather(NodeConverter &converter,
int distance) const
{
/* this uses a modified gaussian blur function otherwise its far too slow */
- CompositorQuality quality = context.getQuality();
+ eCompositorQuality quality = context.getQuality();
/* initialize node data */
NodeBlurData data;
@@ -348,3 +350,5 @@ void KeyingNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputEdges, edgesMatte);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_KeyingNode.h b/source/blender/compositor/nodes/COM_KeyingNode.h
index 64eaaf2a537..6d5e3ca1883 100644
--- a/source/blender/compositor/nodes/COM_KeyingNode.h
+++ b/source/blender/compositor/nodes/COM_KeyingNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief KeyingNode
* \ingroup Node
@@ -58,3 +60,5 @@ class KeyingNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
index 93a9a071226..cbe4f165a45 100644
--- a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
+++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
@@ -22,6 +22,8 @@
#include "DNA_movieclip_types.h"
+namespace blender::compositor {
+
KeyingScreenNode::KeyingScreenNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,3 +47,5 @@ void KeyingScreenNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputScreen, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.h b/source/blender/compositor/nodes/COM_KeyingScreenNode.h
index 2c84b71773e..f2ad3b344f1 100644
--- a/source/blender/compositor/nodes/COM_KeyingScreenNode.h
+++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief KeyingScreenNode
* \ingroup Node
@@ -31,3 +33,5 @@ class KeyingScreenNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.cc b/source/blender/compositor/nodes/COM_LensDistortionNode.cc
index 34d2fba6433..f5226d31989 100644
--- a/source/blender/compositor/nodes/COM_LensDistortionNode.cc
+++ b/source/blender/compositor/nodes/COM_LensDistortionNode.cc
@@ -21,6 +21,8 @@
#include "COM_ProjectorLensDistortionOperation.h"
#include "COM_ScreenLensDistortionOperation.h"
+namespace blender::compositor {
+
LensDistortionNode::LensDistortionNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -59,3 +61,5 @@ void LensDistortionNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.h b/source/blender/compositor/nodes/COM_LensDistortionNode.h
index 866e2ec8884..4de1b0fe4da 100644
--- a/source/blender/compositor/nodes/COM_LensDistortionNode.h
+++ b/source/blender/compositor/nodes/COM_LensDistortionNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief LensDistortionNode
* \ingroup Node
@@ -30,3 +32,5 @@ class LensDistortionNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
index 8bfea1eff49..920da53231f 100644
--- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
@@ -22,6 +22,8 @@
#include "COM_LuminanceMatteOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
LuminanceMatteNode::LuminanceMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -51,3 +53,5 @@ void LuminanceMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.h b/source/blender/compositor/nodes/COM_LuminanceMatteNode.h
index 148a0d302d4..ef4ebc8ad92 100644
--- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.h
+++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief LuminanceMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class LuminanceMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.cc b/source/blender/compositor/nodes/COM_MapRangeNode.cc
index 352bc0dd48d..718a6d9e47b 100644
--- a/source/blender/compositor/nodes/COM_MapRangeNode.cc
+++ b/source/blender/compositor/nodes/COM_MapRangeNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MapRangeOperation.h"
+namespace blender::compositor {
+
MapRangeNode::MapRangeNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -47,3 +49,5 @@ void MapRangeNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(destMaxSocket, operation->getInputSocket(4));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.h b/source/blender/compositor/nodes/COM_MapRangeNode.h
index 55c7020635a..ad6fd78a7d5 100644
--- a/source/blender/compositor/nodes/COM_MapRangeNode.h
+++ b/source/blender/compositor/nodes/COM_MapRangeNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief MapRangeNode
* \ingroup Node
@@ -30,3 +33,5 @@ class MapRangeNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc
index feb9c75ec56..4b7a9e8af0f 100644
--- a/source/blender/compositor/nodes/COM_MapUVNode.cc
+++ b/source/blender/compositor/nodes/COM_MapUVNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MapUVOperation.h"
+namespace blender::compositor {
+
MapUVNode::MapUVNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -39,3 +41,5 @@ void MapUVNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapUVNode.h b/source/blender/compositor/nodes/COM_MapUVNode.h
index ff565372d71..f7f4db167ea 100644
--- a/source/blender/compositor/nodes/COM_MapUVNode.h
+++ b/source/blender/compositor/nodes/COM_MapUVNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief MapUVNode
* \ingroup Node
@@ -30,3 +32,5 @@ class MapUVNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapValueNode.cc b/source/blender/compositor/nodes/COM_MapValueNode.cc
index e07df8ad367..ae48bda6cb8 100644
--- a/source/blender/compositor/nodes/COM_MapValueNode.cc
+++ b/source/blender/compositor/nodes/COM_MapValueNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MapValueOperation.h"
+namespace blender::compositor {
+
MapValueNode::MapValueNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -41,3 +43,5 @@ void MapValueNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(colorSocket, convertProg->getInputSocket(0));
converter.mapOutputSocket(valueSocket, convertProg->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapValueNode.h b/source/blender/compositor/nodes/COM_MapValueNode.h
index a8a5028a71f..dcac1d6e3c5 100644
--- a/source/blender/compositor/nodes/COM_MapValueNode.h
+++ b/source/blender/compositor/nodes/COM_MapValueNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief MapValueNode
* \ingroup Node
@@ -30,3 +33,5 @@ class MapValueNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc
index a6415a3992e..ef171c01653 100644
--- a/source/blender/compositor/nodes/COM_MaskNode.cc
+++ b/source/blender/compositor/nodes/COM_MaskNode.cc
@@ -22,6 +22,8 @@
#include "DNA_mask_types.h"
+namespace blender::compositor {
+
MaskNode::MaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -68,3 +70,5 @@ void MaskNode::convertToOperations(NodeConverter &converter,
converter.addOperation(operation);
converter.mapOutputSocket(outputMask, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MaskNode.h b/source/blender/compositor/nodes/COM_MaskNode.h
index 3d813fb61f2..5890cf5957a 100644
--- a/source/blender/compositor/nodes/COM_MaskNode.h
+++ b/source/blender/compositor/nodes/COM_MaskNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief MaskNode
* \ingroup Node
@@ -31,3 +33,5 @@ class MaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MathNode.cc b/source/blender/compositor/nodes/COM_MathNode.cc
index 0edf880400f..dd0d8931d58 100644
--- a/source/blender/compositor/nodes/COM_MathNode.cc
+++ b/source/blender/compositor/nodes/COM_MathNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MathBaseOperation.h"
+namespace blender::compositor {
+
void MathNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -159,3 +161,5 @@ void MathNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MathNode.h b/source/blender/compositor/nodes/COM_MathNode.h
index a7036e9fd36..5db59e62bab 100644
--- a/source/blender/compositor/nodes/COM_MathNode.h
+++ b/source/blender/compositor/nodes/COM_MathNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief MathNode
* \ingroup Node
@@ -32,3 +34,5 @@ class MathNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MixNode.cc b/source/blender/compositor/nodes/COM_MixNode.cc
index d082590d21b..cfa8d0ee6a6 100644
--- a/source/blender/compositor/nodes/COM_MixNode.cc
+++ b/source/blender/compositor/nodes/COM_MixNode.cc
@@ -24,6 +24,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_material_types.h" /* the ramp types */
+namespace blender::compositor {
+
MixNode::MixNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -110,3 +112,5 @@ void MixNode::convertToOperations(NodeConverter &converter,
converter.addPreview(convertProg->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MixNode.h b/source/blender/compositor/nodes/COM_MixNode.h
index 6f4a7a2cb88..81f9c41871e 100644
--- a/source/blender/compositor/nodes/COM_MixNode.h
+++ b/source/blender/compositor/nodes/COM_MixNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief MixNode
* \ingroup Node
@@ -30,3 +33,5 @@ class MixNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc
index 7cc8f2ea19c..50bd9b4d71b 100644
--- a/source/blender/compositor/nodes/COM_MovieClipNode.cc
+++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc
@@ -29,6 +29,8 @@
#include "IMB_imbuf.h"
+namespace blender::compositor {
+
MovieClipNode::MovieClipNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -106,3 +108,5 @@ void MovieClipNode::convertToOperations(NodeConverter &converter,
IMB_freeImBuf(ibuf);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.h b/source/blender/compositor/nodes/COM_MovieClipNode.h
index c46033f944a..a469ce9e2a4 100644
--- a/source/blender/compositor/nodes/COM_MovieClipNode.h
+++ b/source/blender/compositor/nodes/COM_MovieClipNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief MovieClipNode
* \ingroup Node
@@ -31,3 +33,5 @@ class MovieClipNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
index ebace6d5fff..8f17ef8bb98 100644
--- a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
+++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
@@ -22,6 +22,8 @@
#include "COM_MovieDistortionOperation.h"
#include "DNA_movieclip_types.h"
+namespace blender::compositor {
+
MovieDistortionNode::MovieDistortionNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -44,3 +46,5 @@ void MovieDistortionNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.h b/source/blender/compositor/nodes/COM_MovieDistortionNode.h
index 60349be0793..0c1610aa3d3 100644
--- a/source/blender/compositor/nodes/COM_MovieDistortionNode.h
+++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief MovieDistortionNode
* \ingroup Node
@@ -30,3 +32,5 @@ class MovieDistortionNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_NormalNode.cc b/source/blender/compositor/nodes/COM_NormalNode.cc
index 1f48a26fd75..5a97b0932ef 100644
--- a/source/blender/compositor/nodes/COM_NormalNode.cc
+++ b/source/blender/compositor/nodes/COM_NormalNode.cc
@@ -22,6 +22,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_SetVectorOperation.h"
+namespace blender::compositor {
+
NormalNode::NormalNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -54,3 +56,5 @@ void NormalNode::convertToOperations(NodeConverter &converter,
converter.addLink(operationSet->getOutputSocket(0), operation->getInputSocket(1));
converter.mapOutputSocket(outputSocketDotproduct, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_NormalNode.h b/source/blender/compositor/nodes/COM_NormalNode.h
index 9b32e57a141..6d5cbb394a0 100644
--- a/source/blender/compositor/nodes/COM_NormalNode.h
+++ b/source/blender/compositor/nodes/COM_NormalNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief NormalNode
* \ingroup Node
@@ -30,3 +32,5 @@ class NormalNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.cc b/source/blender/compositor/nodes/COM_NormalizeNode.cc
index 72459fd477c..639dd8e5a51 100644
--- a/source/blender/compositor/nodes/COM_NormalizeNode.cc
+++ b/source/blender/compositor/nodes/COM_NormalizeNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_NormalizeOperation.h"
+namespace blender::compositor {
+
NormalizeNode::NormalizeNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -34,3 +36,5 @@ void NormalizeNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.h b/source/blender/compositor/nodes/COM_NormalizeNode.h
index bf8055245e7..7770fc49b61 100644
--- a/source/blender/compositor/nodes/COM_NormalizeNode.h
+++ b/source/blender/compositor/nodes/COM_NormalizeNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief NormalizeNode
* \ingroup Node
@@ -30,3 +32,5 @@ class NormalizeNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc
index dcc1fbdec67..e5b9cfb8cc2 100644
--- a/source/blender/compositor/nodes/COM_OutputFileNode.cc
+++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc
@@ -18,18 +18,44 @@
#include "COM_OutputFileNode.h"
#include "COM_ExecutionSystem.h"
-#include "COM_OutputFileMultiViewOperation.h"
#include "COM_OutputFileOperation.h"
#include "BKE_scene.h"
#include "BLI_path_util.h"
+namespace blender::compositor {
+
OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
}
+void OutputFileNode::add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const
+{
+ for (NodeInput *input : inputs) {
+ NodeImageMultiFileSocket *sockdata =
+ (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
+ /* note: layer becomes an empty placeholder if the input is not linked */
+ operation.add_layer(sockdata->layer, input->getDataType(), input->isLinked());
+ }
+}
+
+void OutputFileNode::map_input_sockets(NodeConverter &converter,
+ OutputOpenExrMultiLayerOperation &operation) const
+{
+ bool previewAdded = false;
+ int index = 0;
+ for (NodeInput *input : inputs) {
+ converter.mapInputSocket(input, operation.getInputSocket(index++));
+
+ if (!previewAdded) {
+ converter.addNodeInputPreview(input);
+ previewAdded = true;
+ }
+ }
+}
+
void OutputFileNode::convertToOperations(NodeConverter &converter,
const CompositorContext &context) const
{
@@ -69,29 +95,15 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
}
converter.addOperation(outputOperation);
- int num_inputs = getNumberOfInputSockets();
- bool previewAdded = false;
- for (int i = 0; i < num_inputs; i++) {
- NodeInput *input = getInputSocket(i);
- NodeImageMultiFileSocket *sockdata =
- (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
-
- /* note: layer becomes an empty placeholder if the input is not linked */
- outputOperation->add_layer(sockdata->layer, input->getDataType(), input->isLinked());
-
- converter.mapInputSocket(input, outputOperation->getInputSocket(i));
-
- if (!previewAdded) {
- converter.addNodeInputPreview(input);
- previewAdded = true;
- }
- }
+ /* First add all inputs. Inputs are stored in a Vector and can be moved to a different
+ * memory address during this time.*/
+ add_input_sockets(*outputOperation);
+ /* After adding the sockets the memory addresses will stick. */
+ map_input_sockets(converter, *outputOperation);
}
else { /* single layer format */
- int num_inputs = getNumberOfInputSockets();
bool previewAdded = false;
- for (int i = 0; i < num_inputs; i++) {
- NodeInput *input = getInputSocket(i);
+ for (NodeInput *input : inputs) {
if (input->isLinked()) {
NodeImageMultiFileSocket *sockdata =
(NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
@@ -151,3 +163,5 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.h b/source/blender/compositor/nodes/COM_OutputFileNode.h
index 2b37a1ae252..c64128a708f 100644
--- a/source/blender/compositor/nodes/COM_OutputFileNode.h
+++ b/source/blender/compositor/nodes/COM_OutputFileNode.h
@@ -19,8 +19,13 @@
#pragma once
#include "COM_Node.h"
+
+#include "COM_OutputFileMultiViewOperation.h"
+
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief OutputFileNode
* \ingroup Node
@@ -30,4 +35,11 @@ class OutputFileNode : public Node {
OutputFileNode(bNode *editorNode);
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
+
+ private:
+ void add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const;
+ void map_input_sockets(NodeConverter &converter,
+ OutputOpenExrMultiLayerOperation &operation) const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_PixelateNode.cc b/source/blender/compositor/nodes/COM_PixelateNode.cc
index f238f68727e..396f339e5a2 100644
--- a/source/blender/compositor/nodes/COM_PixelateNode.cc
+++ b/source/blender/compositor/nodes/COM_PixelateNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_PixelateOperation.h"
+namespace blender::compositor {
+
PixelateNode::PixelateNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -44,3 +46,5 @@ void PixelateNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_PixelateNode.h b/source/blender/compositor/nodes/COM_PixelateNode.h
index 19ae352ce80..1a6555550cf 100644
--- a/source/blender/compositor/nodes/COM_PixelateNode.h
+++ b/source/blender/compositor/nodes/COM_PixelateNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief PixelateNode
* \ingroup Node
@@ -30,3 +32,5 @@ class PixelateNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
index 6b9b51631ec..54a0f4d0452 100644
--- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
+++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
@@ -25,6 +25,8 @@
#include "BKE_node.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -70,3 +72,5 @@ void PlaneTrackDeformNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h
index 29347ec79c4..307738fcaf0 100644
--- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h
+++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h
@@ -23,6 +23,8 @@
#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief PlaneTrackDeformNode
* \ingroup Node
@@ -33,3 +35,5 @@ class PlaneTrackDeformNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc
index ea3eeb13393..851d0366546 100644
--- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc
+++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc
@@ -25,6 +25,8 @@
#include "COM_SetVectorOperation.h"
#include "COM_TranslateOperation.h"
+namespace blender::compositor {
+
RenderLayersNode::RenderLayersNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -72,9 +74,8 @@ void RenderLayersNode::testRenderLink(NodeConverter &converter,
missingRenderLink(converter);
return;
}
- const int num_outputs = this->getNumberOfOutputSockets();
- for (int i = 0; i < num_outputs; i++) {
- NodeOutput *output = this->getOutputSocket(i);
+
+ for (NodeOutput *output : getOutputSockets()) {
NodeImageLayer *storage = (NodeImageLayer *)output->getbNodeSocket()->storage;
RenderPass *rpass = (RenderPass *)BLI_findstring(
&rl->passes, storage->pass_name, offsetof(RenderPass, name));
@@ -153,9 +154,7 @@ void RenderLayersNode::missingSocketLink(NodeConverter &converter, NodeOutput *o
void RenderLayersNode::missingRenderLink(NodeConverter &converter) const
{
- const int num_outputs = this->getNumberOfOutputSockets();
- for (int i = 0; i < num_outputs; i++) {
- NodeOutput *output = this->getOutputSocket(i);
+ for (NodeOutput *output : outputs) {
missingSocketLink(converter, output);
}
}
@@ -174,3 +173,5 @@ void RenderLayersNode::convertToOperations(NodeConverter &converter,
missingRenderLink(converter);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.h b/source/blender/compositor/nodes/COM_RenderLayersNode.h
index 92be0f7daa1..4eb2427c8e0 100644
--- a/source/blender/compositor/nodes/COM_RenderLayersNode.h
+++ b/source/blender/compositor/nodes/COM_RenderLayersNode.h
@@ -23,6 +23,7 @@
#include "DNA_node_types.h"
struct Render;
+namespace blender::compositor {
/**
* \brief RenderLayersNode
@@ -49,3 +50,5 @@ class RenderLayersNode : public Node {
void missingSocketLink(NodeConverter &converter, NodeOutput *output) const;
void missingRenderLink(NodeConverter &converter) const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_RotateNode.cc b/source/blender/compositor/nodes/COM_RotateNode.cc
index cbade778bcb..af5baa733dc 100644
--- a/source/blender/compositor/nodes/COM_RotateNode.cc
+++ b/source/blender/compositor/nodes/COM_RotateNode.cc
@@ -22,6 +22,8 @@
#include "COM_RotateOperation.h"
#include "COM_SetSamplerOperation.h"
+namespace blender::compositor {
+
RotateNode::RotateNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,3 +47,5 @@ void RotateNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_RotateNode.h b/source/blender/compositor/nodes/COM_RotateNode.h
index 0f3b6497cb7..5d8bcb2e3e4 100644
--- a/source/blender/compositor/nodes/COM_RotateNode.h
+++ b/source/blender/compositor/nodes/COM_RotateNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief RotateNode
* \ingroup Node
@@ -30,3 +32,5 @@ class RotateNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc
index 9ffcd5306b0..50d2902f375 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.cc
+++ b/source/blender/compositor/nodes/COM_ScaleNode.cc
@@ -24,6 +24,8 @@
#include "COM_SetSamplerOperation.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
ScaleNode::ScaleNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -79,7 +81,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
operation->setOffset(bnode->custom3, bnode->custom4);
operation->setNewWidth(rd->xsch * render_size_factor);
operation->setNewHeight(rd->ysch * render_size_factor);
- operation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ operation->getInputSocket(0)->setResizeMode(ResizeMode::None);
converter.addOperation(operation);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
@@ -105,3 +107,5 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.h b/source/blender/compositor/nodes/COM_ScaleNode.h
index 5babec2b7aa..186ffa8bdce 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.h
+++ b/source/blender/compositor/nodes/COM_ScaleNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ScaleNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ScaleNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc
index 203aa25c9e9..fcaf52c701d 100644
--- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc
+++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc
@@ -20,6 +20,8 @@
#include "COM_ConvertOperation.h"
+namespace blender::compositor {
+
SeparateColorNode::SeparateColorNode(bNode *editorNode) : Node(editorNode)
{
}
@@ -119,3 +121,5 @@ NodeOperation *SeparateYUVANode::getColorConverter(const CompositorContext & /*c
{
return new ConvertRGBToYUVOperation();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.h b/source/blender/compositor/nodes/COM_SeparateColorNode.h
index 4772c62f93c..eaf543df51f 100644
--- a/source/blender/compositor/nodes/COM_SeparateColorNode.h
+++ b/source/blender/compositor/nodes/COM_SeparateColorNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
class SeparateColorNode : public Node {
public:
SeparateColorNode(bNode *editorNode);
@@ -65,3 +67,5 @@ class SeparateYUVANode : public SeparateColorNode {
NodeOperation *getColorConverter(const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.cc b/source/blender/compositor/nodes/COM_SetAlphaNode.cc
index 233a5e96ff4..dc41c126ba8 100644
--- a/source/blender/compositor/nodes/COM_SetAlphaNode.cc
+++ b/source/blender/compositor/nodes/COM_SetAlphaNode.cc
@@ -21,6 +21,8 @@
#include "COM_SetAlphaMultiplyOperation.h"
#include "COM_SetAlphaReplaceOperation.h"
+namespace blender::compositor {
+
void SetAlphaNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -46,3 +48,5 @@ void SetAlphaNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.h b/source/blender/compositor/nodes/COM_SetAlphaNode.h
index 06dd93012ff..c8d340eb64b 100644
--- a/source/blender/compositor/nodes/COM_SetAlphaNode.h
+++ b/source/blender/compositor/nodes/COM_SetAlphaNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief SetAlphaNode
* \ingroup Node
@@ -32,3 +34,5 @@ class SetAlphaNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.cc b/source/blender/compositor/nodes/COM_SocketProxyNode.cc
index 93d8d399cad..b3aa1770551 100644
--- a/source/blender/compositor/nodes/COM_SocketProxyNode.cc
+++ b/source/blender/compositor/nodes/COM_SocketProxyNode.cc
@@ -25,6 +25,8 @@
#include "COM_SocketProxyOperation.h"
#include "COM_WriteBufferOperation.h"
+namespace blender::compositor {
+
SocketProxyNode::SocketProxyNode(bNode *editorNode,
bNodeSocket *editorInput,
bNodeSocket *editorOutput,
@@ -101,3 +103,5 @@ void SocketBufferNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(input, writeOperation->getInputSocket(0));
converter.mapOutputSocket(output, readOperation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.h b/source/blender/compositor/nodes/COM_SocketProxyNode.h
index 8616fe6dccf..d19fb802767 100644
--- a/source/blender/compositor/nodes/COM_SocketProxyNode.h
+++ b/source/blender/compositor/nodes/COM_SocketProxyNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief SocketProxyNode
* \ingroup Node
@@ -53,3 +55,5 @@ class SocketBufferNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cc b/source/blender/compositor/nodes/COM_SplitViewerNode.cc
index 681adeaf1d3..582c650f205 100644
--- a/source/blender/compositor/nodes/COM_SplitViewerNode.cc
+++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cc
@@ -25,6 +25,8 @@
#include "COM_SplitOperation.h"
#include "COM_ViewerOperation.h"
+namespace blender::compositor {
+
SplitViewerNode::SplitViewerNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -73,3 +75,5 @@ void SplitViewerNode::convertToOperations(NodeConverter &converter,
converter.registerViewer(viewerOperation);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.h b/source/blender/compositor/nodes/COM_SplitViewerNode.h
index 0be586041e9..8a42775eb0d 100644
--- a/source/blender/compositor/nodes/COM_SplitViewerNode.h
+++ b/source/blender/compositor/nodes/COM_SplitViewerNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief SplitViewerNode
* \ingroup Node
@@ -30,3 +33,5 @@ class SplitViewerNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
index 38db080a154..fc72b48eca2 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
@@ -28,6 +28,8 @@
#include "DNA_movieclip_types.h"
+namespace blender::compositor {
+
Stabilize2dNode::Stabilize2dNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -111,3 +113,5 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.h b/source/blender/compositor/nodes/COM_Stabilize2dNode.h
index 2c7cfafb297..34ed8871e33 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.h
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief Stabilize2dNode
* \ingroup Node
@@ -31,3 +33,5 @@ class Stabilize2dNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.cc b/source/blender/compositor/nodes/COM_SunBeamsNode.cc
index d899a54c03c..1e5aa0b8020 100644
--- a/source/blender/compositor/nodes/COM_SunBeamsNode.cc
+++ b/source/blender/compositor/nodes/COM_SunBeamsNode.cc
@@ -18,6 +18,8 @@
#include "COM_SunBeamsNode.h"
#include "COM_SunBeamsOperation.h"
+namespace blender::compositor {
+
SunBeamsNode::SunBeamsNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -37,3 +39,5 @@ void SunBeamsNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.h b/source/blender/compositor/nodes/COM_SunBeamsNode.h
index 8b02f13211f..8b68d3f4cb5 100644
--- a/source/blender/compositor/nodes/COM_SunBeamsNode.h
+++ b/source/blender/compositor/nodes/COM_SunBeamsNode.h
@@ -19,6 +19,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief SunBeamsNode
* \ingroup Node
@@ -29,3 +31,5 @@ class SunBeamsNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SwitchNode.cc b/source/blender/compositor/nodes/COM_SwitchNode.cc
index 947774e98ae..4006d10dafb 100644
--- a/source/blender/compositor/nodes/COM_SwitchNode.cc
+++ b/source/blender/compositor/nodes/COM_SwitchNode.cc
@@ -18,6 +18,8 @@
#include "COM_SwitchNode.h"
+namespace blender::compositor {
+
SwitchNode::SwitchNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void SwitchNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), result);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SwitchNode.h b/source/blender/compositor/nodes/COM_SwitchNode.h
index c8ee503154c..aa6caa2e59f 100644
--- a/source/blender/compositor/nodes/COM_SwitchNode.h
+++ b/source/blender/compositor/nodes/COM_SwitchNode.h
@@ -21,6 +21,9 @@
#include "COM_Node.h"
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief SwitchNode
* \ingroup Node
@@ -31,3 +34,5 @@ class SwitchNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cc b/source/blender/compositor/nodes/COM_SwitchViewNode.cc
index e534ebfac9a..395122dd11b 100644
--- a/source/blender/compositor/nodes/COM_SwitchViewNode.cc
+++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cc
@@ -19,6 +19,8 @@
#include "COM_SwitchViewNode.h"
#include "BLI_listbase.h"
+namespace blender::compositor {
+
SwitchViewNode::SwitchViewNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void SwitchViewNode::convertToOperations(NodeConverter &converter,
result = converter.addInputProxy(getInputSocket(nr), false);
converter.mapOutputSocket(getOutputSocket(0), result);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.h b/source/blender/compositor/nodes/COM_SwitchViewNode.h
index 419fd1b7463..ce6de52182c 100644
--- a/source/blender/compositor/nodes/COM_SwitchViewNode.h
+++ b/source/blender/compositor/nodes/COM_SwitchViewNode.h
@@ -21,6 +21,9 @@
#include "COM_Node.h"
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief SwitchViewNode
* \ingroup Node
@@ -31,3 +34,5 @@ class SwitchViewNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TextureNode.cc b/source/blender/compositor/nodes/COM_TextureNode.cc
index 3381b5098d7..317355b8c9a 100644
--- a/source/blender/compositor/nodes/COM_TextureNode.cc
+++ b/source/blender/compositor/nodes/COM_TextureNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_TextureOperation.h"
+namespace blender::compositor {
+
TextureNode::TextureNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -54,3 +56,5 @@ void TextureNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), alphaOperation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), alphaOperation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TextureNode.h b/source/blender/compositor/nodes/COM_TextureNode.h
index 5a9a56b748d..b886e3b74e1 100644
--- a/source/blender/compositor/nodes/COM_TextureNode.h
+++ b/source/blender/compositor/nodes/COM_TextureNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief TextureNode
* \ingroup Node
@@ -31,3 +33,5 @@ class TextureNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TimeNode.cc b/source/blender/compositor/nodes/COM_TimeNode.cc
index 247e0d11df6..c14c5344eee 100644
--- a/source/blender/compositor/nodes/COM_TimeNode.cc
+++ b/source/blender/compositor/nodes/COM_TimeNode.cc
@@ -24,6 +24,8 @@
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
TimeNode::TimeNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -56,3 +58,5 @@ void TimeNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TimeNode.h b/source/blender/compositor/nodes/COM_TimeNode.h
index 0f2b624fd1f..5688e2cff03 100644
--- a/source/blender/compositor/nodes/COM_TimeNode.h
+++ b/source/blender/compositor/nodes/COM_TimeNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief TimeNode
* \ingroup Node
@@ -30,3 +32,5 @@ class TimeNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TonemapNode.cc b/source/blender/compositor/nodes/COM_TonemapNode.cc
index db329e56f9b..844fe3e8cb6 100644
--- a/source/blender/compositor/nodes/COM_TonemapNode.cc
+++ b/source/blender/compositor/nodes/COM_TonemapNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_TonemapOperation.h"
+namespace blender::compositor {
+
TonemapNode::TonemapNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void TonemapNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TonemapNode.h b/source/blender/compositor/nodes/COM_TonemapNode.h
index db7ae5fa290..cac9004c32a 100644
--- a/source/blender/compositor/nodes/COM_TonemapNode.h
+++ b/source/blender/compositor/nodes/COM_TonemapNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief TonemapNode
* \ingroup Node
@@ -30,3 +32,5 @@ class TonemapNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.cc b/source/blender/compositor/nodes/COM_TrackPositionNode.cc
index 52e7f7d832b..3fb5fc02f20 100644
--- a/source/blender/compositor/nodes/COM_TrackPositionNode.cc
+++ b/source/blender/compositor/nodes/COM_TrackPositionNode.cc
@@ -26,6 +26,8 @@
#include "BKE_node.h"
+namespace blender::compositor {
+
TrackPositionNode::TrackPositionNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -109,3 +111,5 @@ void TrackPositionNode::convertToOperations(NodeConverter &converter,
converter.addLink(operationMotionPostY->getOutputSocket(), combine_operation->getInputSocket(3));
converter.mapOutputSocket(outputSpeed, combine_operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.h b/source/blender/compositor/nodes/COM_TrackPositionNode.h
index f2062828ca5..665ba36fe09 100644
--- a/source/blender/compositor/nodes/COM_TrackPositionNode.h
+++ b/source/blender/compositor/nodes/COM_TrackPositionNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief TrackPositionNode
* \ingroup Node
@@ -31,3 +33,5 @@ class TrackPositionNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc
index cd5ba8ba201..cd12939ab43 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.cc
+++ b/source/blender/compositor/nodes/COM_TransformNode.cc
@@ -24,6 +24,8 @@
#include "COM_SetValueOperation.h"
#include "COM_TranslateOperation.h"
+namespace blender::compositor {
+
TransformNode::TransformNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -66,3 +68,5 @@ void TransformNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TransformNode.h b/source/blender/compositor/nodes/COM_TransformNode.h
index 56d05cd4c94..137e162256d 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.h
+++ b/source/blender/compositor/nodes/COM_TransformNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief TransformNode
* \ingroup Node
@@ -31,3 +33,5 @@ class TransformNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc
index 13a73953ea0..1b2ce341a66 100644
--- a/source/blender/compositor/nodes/COM_TranslateNode.cc
+++ b/source/blender/compositor/nodes/COM_TranslateNode.cc
@@ -23,6 +23,8 @@
#include "COM_WrapOperation.h"
#include "COM_WriteBufferOperation.h"
+namespace blender::compositor {
+
TranslateNode::TranslateNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -54,7 +56,10 @@ void TranslateNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputYSocket, operation->getInputSocket(2));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
- if (data->wrap_axis) {
+ /* FullFrame does not support using WriteBufferOperation.
+ * TODO: Implement TranslateOperation with wrap support in FullFrame.
+ */
+ if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) {
WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color);
WrapOperation *wrapOperation = new WrapOperation(DataType::Color);
wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy());
@@ -69,3 +74,5 @@ void TranslateNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TranslateNode.h b/source/blender/compositor/nodes/COM_TranslateNode.h
index e6fd230572c..0cea234bff8 100644
--- a/source/blender/compositor/nodes/COM_TranslateNode.h
+++ b/source/blender/compositor/nodes/COM_TranslateNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief TranslateNode
* \ingroup Node
@@ -30,3 +32,5 @@ class TranslateNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ValueNode.cc b/source/blender/compositor/nodes/COM_ValueNode.cc
index 4227db0d10e..6b640fa2a3a 100644
--- a/source/blender/compositor/nodes/COM_ValueNode.cc
+++ b/source/blender/compositor/nodes/COM_ValueNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
ValueNode::ValueNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -35,3 +37,5 @@ void ValueNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(output, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ValueNode.h b/source/blender/compositor/nodes/COM_ValueNode.h
index 18162b7d4b8..1401b2c7e0a 100644
--- a/source/blender/compositor/nodes/COM_ValueNode.h
+++ b/source/blender/compositor/nodes/COM_ValueNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ValueNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ValueNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.cc b/source/blender/compositor/nodes/COM_VectorBlurNode.cc
index a92991c8b49..7aa5f5668c9 100644
--- a/source/blender/compositor/nodes/COM_VectorBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_VectorBlurNode.cc
@@ -20,6 +20,8 @@
#include "COM_VectorBlurOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
VectorBlurNode::VectorBlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -41,3 +43,5 @@ void VectorBlurNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.h b/source/blender/compositor/nodes/COM_VectorBlurNode.h
index 2a55ca4457d..8c98a0b81a1 100644
--- a/source/blender/compositor/nodes/COM_VectorBlurNode.h
+++ b/source/blender/compositor/nodes/COM_VectorBlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief VectorBlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class VectorBlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.cc b/source/blender/compositor/nodes/COM_VectorCurveNode.cc
index 1201a9f9613..f2fd80cd93e 100644
--- a/source/blender/compositor/nodes/COM_VectorCurveNode.cc
+++ b/source/blender/compositor/nodes/COM_VectorCurveNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_VectorCurveOperation.h"
+namespace blender::compositor {
+
VectorCurveNode::VectorCurveNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -35,3 +37,5 @@ void VectorCurveNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.h b/source/blender/compositor/nodes/COM_VectorCurveNode.h
index 4e84b7454d9..ee4df5d3a42 100644
--- a/source/blender/compositor/nodes/COM_VectorCurveNode.h
+++ b/source/blender/compositor/nodes/COM_VectorCurveNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief VectorCurveNode
* \ingroup Node
@@ -30,3 +32,5 @@ class VectorCurveNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc
index 7b86fb1d64d..dc454b95080 100644
--- a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc
+++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc
@@ -22,6 +22,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
ViewLevelsNode::ViewLevelsNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -59,3 +61,5 @@ void ViewLevelsNode::convertToOperations(NodeConverter &converter,
converter.addOutputValue(getOutputSocket(1), 0.0f);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.h b/source/blender/compositor/nodes/COM_ViewLevelsNode.h
index 20dbe967286..055d871498e 100644
--- a/source/blender/compositor/nodes/COM_ViewLevelsNode.h
+++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ViewLevelsNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ViewLevelsNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc
index 359c3d3031d..3833a8d7ca8 100644
--- a/source/blender/compositor/nodes/COM_ViewerNode.cc
+++ b/source/blender/compositor/nodes/COM_ViewerNode.cc
@@ -25,6 +25,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_ViewerOperation.h"
+namespace blender::compositor {
+
ViewerNode::ViewerNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -82,3 +84,5 @@ void ViewerNode::convertToOperations(NodeConverter &converter,
converter.registerViewer(viewerOperation);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ViewerNode.h b/source/blender/compositor/nodes/COM_ViewerNode.h
index 0e13f165794..544a5e6a504 100644
--- a/source/blender/compositor/nodes/COM_ViewerNode.h
+++ b/source/blender/compositor/nodes/COM_ViewerNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief ViewerNode
* \ingroup Node
@@ -30,3 +33,5 @@ class ViewerNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc
index b61c018d029..a76049ff249 100644
--- a/source/blender/compositor/nodes/COM_ZCombineNode.cc
+++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc
@@ -28,6 +28,8 @@
#include "DNA_material_types.h" /* the ramp types */
+namespace blender::compositor {
+
void ZCombineNode::convertToOperations(NodeConverter &converter,
const CompositorContext &context) const
{
@@ -99,3 +101,5 @@ void ZCombineNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(1), zoperation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.h b/source/blender/compositor/nodes/COM_ZCombineNode.h
index aa8bd979691..82f2f30fb3c 100644
--- a/source/blender/compositor/nodes/COM_ZCombineNode.h
+++ b/source/blender/compositor/nodes/COM_ZCombineNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ZCombineNode
* \ingroup Node
@@ -32,3 +34,5 @@ class ZCombineNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
index 668d07c7c3d..0c656753a51 100644
--- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
@@ -18,10 +18,7 @@
#include "COM_AlphaOverKeyOperation.h"
-AlphaOverKeyOperation::AlphaOverKeyOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
void AlphaOverKeyOperation::executePixelSampled(float output[4],
float x,
@@ -52,3 +49,5 @@ void AlphaOverKeyOperation::executePixelSampled(float output[4],
output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
index b62105d7c41..83713d18971 100644
--- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -27,12 +29,9 @@
class AlphaOverKeyOperation : public MixBaseOperation {
public:
/**
- * Default constructor
- */
- AlphaOverKeyOperation();
-
- /**
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
index b8465ab7ccf..c68c79d2263 100644
--- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
@@ -18,6 +18,8 @@
#include "COM_AlphaOverMixedOperation.h"
+namespace blender::compositor {
+
AlphaOverMixedOperation::AlphaOverMixedOperation()
{
this->m_x = 0.0f;
@@ -53,3 +55,5 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4],
output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
index 3f7137a8d36..e2b3af84162 100644
--- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -44,3 +46,5 @@ class AlphaOverMixedOperation : public MixBaseOperation {
this->m_x = x;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
index 4510c027d46..3dd4607e273 100644
--- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
@@ -18,10 +18,7 @@
#include "COM_AlphaOverPremultiplyOperation.h"
-AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
void AlphaOverPremultiplyOperation::executePixelSampled(float output[4],
float x,
@@ -52,3 +49,5 @@ void AlphaOverPremultiplyOperation::executePixelSampled(float output[4],
output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
index cb60f8393d0..f1d4b668fce 100644
--- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -27,12 +29,9 @@
class AlphaOverPremultiplyOperation : public MixBaseOperation {
public:
/**
- * Default constructor
- */
- AlphaOverPremultiplyOperation();
-
- /**
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc
index 740cd3ff609..23d6f4b80c7 100644
--- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc
+++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc
@@ -24,6 +24,8 @@
#include "RE_texture.h"
+namespace blender::compositor {
+
/* An implementation of the Scale3X edge-extrapolation algorithm.
*
* Code from GIMP plugin, based on code from Adam D. Moss <adam@gimp.org>
@@ -117,7 +119,7 @@ AntiAliasOperation::AntiAliasOperation()
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
this->m_valueReader = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void AntiAliasOperation::initExecution()
@@ -199,3 +201,5 @@ void *AntiAliasOperation::initializeTileData(rcti *rect)
{
return getInputOperation(0)->initializeTileData(rect);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.h b/source/blender/compositor/operations/COM_AntiAliasOperation.h
index 691fb970b11..fc9102b5b4c 100644
--- a/source/blender/compositor/operations/COM_AntiAliasOperation.h
+++ b/source/blender/compositor/operations/COM_AntiAliasOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief AntiAlias operations
* it only supports anti aliasing on BW buffers.
@@ -56,3 +58,5 @@ class AntiAliasOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
index d04fade2e93..64448e2ae95 100644
--- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
@@ -21,12 +21,14 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
BilateralBlurOperation::BilateralBlurOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputColorProgram = nullptr;
this->m_inputDeterminatorProgram = nullptr;
@@ -112,3 +114,5 @@ bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h
index bdc77325aaf..c56cef35050 100644
--- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h
+++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
class BilateralBlurOperation : public NodeOperation, public QualityStepHelper {
private:
SocketReader *m_inputColorProgram;
@@ -55,3 +57,5 @@ class BilateralBlurOperation : public NodeOperation, public QualityStepHelper {
this->m_data = data;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc
index fe6ca1cfd4e..8b73624ca79 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc
@@ -22,13 +22,15 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
BlurBaseOperation::BlurBaseOperation(DataType data_type)
{
/* data_type is almost always DataType::Color except for alpha-blur */
this->addInputSocket(data_type);
this->addInputSocket(DataType::Value);
this->addOutputSocket(data_type);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
memset(&m_data, 0, sizeof(NodeBlurData));
this->m_size = 1.0f;
@@ -167,7 +169,7 @@ void BlurBaseOperation::updateSize()
{
if (!this->m_sizeavailable) {
float result[4];
- this->getInputSocketReader(1)->readSampled(result, 0, 0, COM_PS_NEAREST);
+ this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
this->m_size = result[0];
this->m_sizeavailable = true;
}
@@ -182,3 +184,5 @@ void BlurBaseOperation::determineResolution(unsigned int resolution[2],
resolution[1] += 2 * this->m_size * m_data.sizey;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h
index 84f94dfe124..7937ebd69dc 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.h
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h
@@ -25,6 +25,8 @@
#include "BLI_simd.h"
+namespace blender::compositor {
+
class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
private:
protected:
@@ -76,3 +78,5 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
index 7bb8cd49bfc..3f98732b403 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
@@ -22,15 +22,18 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
BokehBlurOperation::BokehBlurOperation()
{
this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
- this->setOpenCL(true);
+
+ flags.complex = true;
+ flags.open_cl = true;
this->m_size = 1.0f;
this->m_sizeavailable = false;
@@ -76,7 +79,7 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
float tempBoundingBox[4];
float bokeh[4];
- this->m_inputBoundingBoxReader->readSampled(tempBoundingBox, x, y, COM_PS_NEAREST);
+ this->m_inputBoundingBoxReader->readSampled(tempBoundingBox, x, y, PixelSampler::Nearest);
if (tempBoundingBox[0] > 0.0f) {
float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
@@ -90,7 +93,7 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
zero_v4(color_accum);
if (pixelSize < 2) {
- this->m_inputProgram->readSampled(color_accum, x, y, COM_PS_NEAREST);
+ this->m_inputProgram->readSampled(color_accum, x, y, PixelSampler::Nearest);
multiplier_accum[0] = 1.0f;
multiplier_accum[1] = 1.0f;
multiplier_accum[2] = 1.0f;
@@ -106,16 +109,16 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
maxx = MIN2(maxx, input_rect.xmax);
int step = getStep();
- int offsetadd = getOffsetAdd() * COM_NUM_CHANNELS_COLOR;
+ int offsetadd = getOffsetAdd() * COM_DATA_TYPE_COLOR_CHANNELS;
float m = this->m_bokehDimension / pixelSize;
for (int ny = miny; ny < maxy; ny += step) {
- int bufferindex = ((minx - bufferstartx) * COM_NUM_CHANNELS_COLOR) +
- ((ny - bufferstarty) * COM_NUM_CHANNELS_COLOR * bufferwidth);
+ int bufferindex = ((minx - bufferstartx) * COM_DATA_TYPE_COLOR_CHANNELS) +
+ ((ny - bufferstarty) * COM_DATA_TYPE_COLOR_CHANNELS * bufferwidth);
for (int nx = minx; nx < maxx; nx += step) {
float u = this->m_bokehMidX - (nx - x) * m;
float v = this->m_bokehMidY - (ny - y) * m;
- this->m_inputBokehProgram->readSampled(bokeh, u, v, COM_PS_NEAREST);
+ this->m_inputBokehProgram->readSampled(bokeh, u, v, PixelSampler::Nearest);
madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]);
add_v4_v4(multiplier_accum, bokeh);
bufferindex += offsetadd;
@@ -127,7 +130,7 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
output[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
}
else {
- this->m_inputProgram->readSampled(output, x, y, COM_PS_NEAREST);
+ this->m_inputProgram->readSampled(output, x, y, PixelSampler::Nearest);
}
}
@@ -224,7 +227,7 @@ void BokehBlurOperation::updateSize()
{
if (!this->m_sizeavailable) {
float result[4];
- this->getInputSocketReader(3)->readSampled(result, 0, 0, COM_PS_NEAREST);
+ this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
this->m_size = result[0];
CLAMP(this->m_size, 0.0f, 10.0f);
this->m_sizeavailable = true;
@@ -241,3 +244,5 @@ void BokehBlurOperation::determineResolution(unsigned int resolution[2],
resolution[1] += 2 * this->m_size * max_dim / 100.0f;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h
index 9bdf8bc4035..3ce06adb5d6 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
private:
SocketReader *m_inputProgram;
@@ -78,3 +80,5 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc
index 01f8c81b3b7..63f283b6acc 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc
@@ -19,6 +19,8 @@
#include "COM_BokehImageOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
BokehImageOperation::BokehImageOperation()
{
this->addOutputSocket(DataType::Color);
@@ -124,3 +126,5 @@ void BokehImageOperation::determineResolution(unsigned int resolution[2],
resolution[0] = COM_BLUR_BOKEH_PIXELS;
resolution[1] = COM_BLUR_BOKEH_PIXELS;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h
index 34fd4f6411b..2e0bc8a34dc 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.h
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* \brief The BokehImageOperation class is an operation that creates an image useful to mimic the
*internals of a camera.
@@ -150,3 +152,5 @@ class BokehImageOperation : public NodeOperation {
this->m_deleteData = true;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc
index 51b1ea98456..9938d4a85ed 100644
--- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
BoxMaskOperation::BoxMaskOperation()
{
this->addInputSocket(DataType::Value);
@@ -108,3 +110,5 @@ void BoxMaskOperation::deinitExecution()
this->m_inputMask = nullptr;
this->m_inputValue = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h
index 6ffa7d98aff..fdec7bdd8ca 100644
--- a/source/blender/compositor/operations/COM_BoxMaskOperation.h
+++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class BoxMaskOperation : public NodeOperation {
private:
/**
@@ -63,3 +65,5 @@ class BoxMaskOperation : public NodeOperation {
this->m_maskType = maskType;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc
index 3a6ddd178e8..92cab47318a 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.cc
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc
@@ -18,6 +18,8 @@
#include "COM_BrightnessOperation.h"
+namespace blender::compositor {
+
BrightnessOperation::BrightnessOperation()
{
this->addInputSocket(DataType::Color);
@@ -89,3 +91,5 @@ void BrightnessOperation::deinitExecution()
this->m_inputBrightnessProgram = nullptr;
this->m_inputContrastProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.h b/source/blender/compositor/operations/COM_BrightnessOperation.h
index fa2c80ab3f1..7c33e0b35ec 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.h
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class BrightnessOperation : public NodeOperation {
private:
/**
@@ -51,3 +53,5 @@ class BrightnessOperation : public NodeOperation {
void setUsePremultiply(bool use_premultiply);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
index fdeecd0cb29..a7ea49aed8d 100644
--- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
+++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
@@ -22,14 +22,16 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
CalculateMeanOperation::CalculateMeanOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Value);
this->m_imageReader = nullptr;
this->m_iscalculated = false;
this->m_setting = 1;
- this->setComplex(true);
+ this->flags.complex = true;
}
void CalculateMeanOperation::initExecution()
{
@@ -125,3 +127,5 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile)
}
this->m_result = sum / pixels;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.h b/source/blender/compositor/operations/COM_CalculateMeanOperation.h
index e1a5bc3fd75..8b3bf281c93 100644
--- a/source/blender/compositor/operations/COM_CalculateMeanOperation.h
+++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief base class of CalculateMean, implementing the simple CalculateMean
* \ingroup operation
@@ -67,3 +69,5 @@ class CalculateMeanOperation : public NodeOperation {
protected:
void calculateMean(MemoryBuffer *tile);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
index 9a1e48177ed..ed554b9ac06 100644
--- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
+++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
@@ -22,10 +22,7 @@
#include "IMB_colormanagement.h"
-CalculateStandardDeviationOperation::CalculateStandardDeviationOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
void CalculateStandardDeviationOperation::executePixel(float output[4],
int /*x*/,
@@ -98,3 +95,5 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect)
unlockMutex();
return nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
index f377f96418a..bc4aca69546 100644
--- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
+++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
@@ -21,6 +21,9 @@
#include "COM_CalculateMeanOperation.h"
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief base class of CalculateStandardDeviation,
* implementing the simple CalculateStandardDeviation.
@@ -31,8 +34,6 @@ class CalculateStandardDeviationOperation : public CalculateMeanOperation {
float m_standardDeviation;
public:
- CalculateStandardDeviationOperation();
-
/**
* The inner loop of this operation.
*/
@@ -40,3 +41,5 @@ class CalculateStandardDeviationOperation : public CalculateMeanOperation {
void *initializeTileData(rcti *rect) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
index e53a416f0dd..eee007ce9e6 100644
--- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
+++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
@@ -18,6 +18,8 @@
#include "COM_ChangeHSVOperation.h"
+namespace blender::compositor {
+
ChangeHSVOperation::ChangeHSVOperation()
{
this->addInputSocket(DataType::Color);
@@ -68,3 +70,5 @@ void ChangeHSVOperation::executePixelSampled(float output[4],
output[2] = inputColor1[2] * value[0];
output[3] = inputColor1[3];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h
index f448956df16..d38b4be3efe 100644
--- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h
+++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -45,3 +47,5 @@ class ChangeHSVOperation : public NodeOperation {
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
index 98105a9dfde..89290978608 100644
--- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ChannelMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
ChannelMatteOperation::ChannelMatteOperation()
{
addInputSocket(DataType::Color);
@@ -118,3 +120,5 @@ void ChannelMatteOperation::executePixelSampled(float output[4],
/* Don't make something that was more transparent less transparent. */
output[0] = MIN2(alpha, inColor[3]);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h
index b295f7709d6..6e9dcccd36e 100644
--- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -70,3 +72,5 @@ class ChannelMatteOperation : public NodeOperation {
this->m_matte_channel = custom2;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
index 09bf9c76e55..69aa4aac163 100644
--- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ChromaMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
ChromaMatteOperation::ChromaMatteOperation()
{
addInputSocket(DataType::Color);
@@ -107,3 +109,5 @@ void ChromaMatteOperation::executePixelSampled(float output[4],
output[0] = inImage[3]; /* make pixel just as transparent as it was before */
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h
index 11f8c540a4b..48c3a785011 100644
--- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -49,3 +51,5 @@ class ChromaMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
index b21e453699b..d1d3752e402 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ColorBalanceASCCDLOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
inline float colorbalance_cdl(float in, float offset, float power, float slope)
{
float x = in * slope + offset;
@@ -79,3 +81,5 @@ void ColorBalanceASCCDLOperation::deinitExecution()
this->m_inputValueOperation = nullptr;
this->m_inputColorOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h
index b7ee5d341ad..5851600190f 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h
+++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -70,3 +72,5 @@ class ColorBalanceASCCDLOperation : public NodeOperation {
copy_v3_v3(this->m_slope, slope);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
index b6ff636bce9..cac16a3f7b0 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ColorBalanceLGGOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
inline float colorbalance_lgg(float in, float lift_lgg, float gamma_inv, float gain)
{
/* 1:1 match with the sequencer with linear/srgb conversions, the conversion isn't pretty
@@ -84,3 +86,5 @@ void ColorBalanceLGGOperation::deinitExecution()
this->m_inputValueOperation = nullptr;
this->m_inputColorOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h
index 4b2db3da134..23f70247b66 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h
+++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -70,3 +72,5 @@ class ColorBalanceLGGOperation : public NodeOperation {
copy_v3_v3(this->m_gamma_inv, gamma_inv);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc
index b2578451180..168e9b57eb2 100644
--- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc
@@ -21,6 +21,8 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
ColorCorrectionOperation::ColorCorrectionOperation()
{
this->addInputSocket(DataType::Color);
@@ -160,3 +162,5 @@ void ColorCorrectionOperation::deinitExecution()
this->m_inputImage = nullptr;
this->m_inputMask = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h
index be0c9c198cf..c5826ed0152 100644
--- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h
+++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ColorCorrectionOperation : public NodeOperation {
private:
/**
@@ -68,3 +70,5 @@ class ColorCorrectionOperation : public NodeOperation {
this->m_blueChannelEnabled = enabled;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc
index 35218cef7cc..cb0565a81a2 100644
--- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc
@@ -22,6 +22,8 @@
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
ColorCurveOperation::ColorCurveOperation()
{
this->addInputSocket(DataType::Value);
@@ -151,3 +153,5 @@ void ConstantLevelColorCurveOperation::deinitExecution()
this->m_inputFacProgram = nullptr;
this->m_inputImageProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h
index d57ff81bbe7..6fc7759b8d2 100644
--- a/source/blender/compositor/operations/COM_ColorCurveOperation.h
+++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_color_types.h"
+namespace blender::compositor {
+
class ColorCurveOperation : public CurveBaseOperation {
private:
/**
@@ -88,3 +90,5 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation {
copy_v3_v3(this->m_white, white);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.cc b/source/blender/compositor/operations/COM_ColorExposureOperation.cc
index 0e24620846e..1512ff87658 100644
--- a/source/blender/compositor/operations/COM_ColorExposureOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorExposureOperation.cc
@@ -18,6 +18,8 @@
#include "COM_ColorExposureOperation.h"
+namespace blender::compositor {
+
ExposureOperation::ExposureOperation()
{
this->addInputSocket(DataType::Color);
@@ -55,3 +57,5 @@ void ExposureOperation::deinitExecution()
this->m_inputProgram = nullptr;
this->m_inputExposureProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.h b/source/blender/compositor/operations/COM_ColorExposureOperation.h
index 981caeb2663..0cfaa059e41 100644
--- a/source/blender/compositor/operations/COM_ColorExposureOperation.h
+++ b/source/blender/compositor/operations/COM_ColorExposureOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ExposureOperation : public NodeOperation {
private:
/**
@@ -46,3 +48,5 @@ class ExposureOperation : public NodeOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc
index cc7a81f68d1..89f56ac4aae 100644
--- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ColorMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
ColorMatteOperation::ColorMatteOperation()
{
addInputSocket(DataType::Color);
@@ -79,3 +81,5 @@ void ColorMatteOperation::executePixelSampled(float output[4],
output[0] = inColor[3]; /* make pixel just as transparent as it was before */
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h
index 643dc83b3d9..439a3b0741d 100644
--- a/source/blender/compositor/operations/COM_ColorMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -49,3 +51,5 @@ class ColorMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc
index 4fde951fd3c..0ee65a6529e 100644
--- a/source/blender/compositor/operations/COM_ColorRampOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc
@@ -20,6 +20,8 @@
#include "BKE_colorband.h"
+namespace blender::compositor {
+
ColorRampOperation::ColorRampOperation()
{
this->addInputSocket(DataType::Value);
@@ -48,3 +50,5 @@ void ColorRampOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.h b/source/blender/compositor/operations/COM_ColorRampOperation.h
index 8e725ccf709..d32af9bea24 100644
--- a/source/blender/compositor/operations/COM_ColorRampOperation.h
+++ b/source/blender/compositor/operations/COM_ColorRampOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_texture_types.h"
+namespace blender::compositor {
+
class ColorRampOperation : public NodeOperation {
private:
/**
@@ -52,3 +54,5 @@ class ColorRampOperation : public NodeOperation {
this->m_colorBand = colorBand;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc
index 8e8eaabfc6b..7dc7e2775fc 100644
--- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#define AVG(a, b) ((a + b) / 2)
+namespace blender::compositor {
+
ColorSpillOperation::ColorSpillOperation()
{
addInputSocket(DataType::Color);
@@ -115,3 +117,5 @@ void ColorSpillOperation::executePixelSampled(float output[4],
copy_v4_v4(output, input);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h
index e8cd15fb3bc..9b82e720527 100644
--- a/source/blender/compositor/operations/COM_ColorSpillOperation.h
+++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -64,3 +66,5 @@ class ColorSpillOperation : public NodeOperation {
float calculateMapValue(float fac, float *input);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc
index 0454bae8b38..94d41b28f5d 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.cc
+++ b/source/blender/compositor/operations/COM_CompositorOperation.cc
@@ -31,6 +31,8 @@
#include "PIL_time.h"
+namespace blender::compositor {
+
CompositorOperation::CompositorOperation()
{
this->addInputSocket(DataType::Color);
@@ -50,6 +52,8 @@ CompositorOperation::CompositorOperation()
this->m_scene = nullptr;
this->m_sceneName[0] = '\0';
this->m_viewName = nullptr;
+
+ flags.use_render_border = true;
}
void CompositorOperation::initExecution()
@@ -147,7 +151,7 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
int y2 = rect->ymax;
int offset = (y1 * this->getWidth() + x1);
int add = (this->getWidth() - (x2 - x1));
- int offset4 = offset * COM_NUM_CHANNELS_COLOR;
+ int offset4 = offset * COM_DATA_TYPE_COLOR_CHANNELS;
int x;
int y;
bool breaked = false;
@@ -196,23 +200,23 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
for (x = x1; x < x2 && (!breaked); x++) {
int input_x = x + dx, input_y = y + dy;
- this->m_imageInput->readSampled(color, input_x, input_y, COM_PS_NEAREST);
+ this->m_imageInput->readSampled(color, input_x, input_y, PixelSampler::Nearest);
if (this->m_useAlphaInput) {
- this->m_alphaInput->readSampled(&(color[3]), input_x, input_y, COM_PS_NEAREST);
+ this->m_alphaInput->readSampled(&(color[3]), input_x, input_y, PixelSampler::Nearest);
}
copy_v4_v4(buffer + offset4, color);
- this->m_depthInput->readSampled(color, input_x, input_y, COM_PS_NEAREST);
+ this->m_depthInput->readSampled(color, input_x, input_y, PixelSampler::Nearest);
zbuffer[offset] = color[0];
- offset4 += COM_NUM_CHANNELS_COLOR;
+ offset4 += COM_DATA_TYPE_COLOR_CHANNELS;
offset++;
if (isBraked()) {
breaked = true;
}
}
offset += add;
- offset4 += add * COM_NUM_CHANNELS_COLOR;
+ offset4 += add * COM_DATA_TYPE_COLOR_CHANNELS;
}
}
@@ -242,3 +246,5 @@ void CompositorOperation::determineResolution(unsigned int resolution[2],
resolution[0] = width;
resolution[1] = height;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h
index 1c964a80093..65988c86cc5 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.h
+++ b/source/blender/compositor/operations/COM_CompositorOperation.h
@@ -24,6 +24,8 @@
struct Scene;
+namespace blender::compositor {
+
/**
* \brief Compositor output operation
*/
@@ -109,9 +111,9 @@ class CompositorOperation : public NodeOperation {
}
void initExecution() override;
void deinitExecution() override;
- CompositorPriority getRenderPriority() const override
+ eCompositorPriority getRenderPriority() const override
{
- return CompositorPriority::Medium;
+ return eCompositorPriority::Medium;
}
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
@@ -124,3 +126,5 @@ class CompositorOperation : public NodeOperation {
this->m_active = active;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc
index 431f0e9c880..c00fe5d5f61 100644
--- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc
@@ -20,6 +20,8 @@
#include "IMB_imbuf.h"
+namespace blender::compositor {
+
ConvertColorProfileOperation::ConvertColorProfileOperation()
{
this->addInputSocket(DataType::Color);
@@ -48,3 +50,5 @@ void ConvertColorProfileOperation::deinitExecution()
{
this->m_inputOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h
index 927a5a7fef1..6162408501b 100644
--- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h
+++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -80,3 +82,5 @@ class ConvertColorProfileOperation : public NodeOperation {
this->m_predivided = predivided;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc
index 17b351cb14c..57027c11949 100644
--- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc
@@ -21,6 +21,8 @@
#include "BLI_math.h"
#include "DNA_camera_types.h"
+namespace blender::compositor {
+
ConvertDepthToRadiusOperation::ConvertDepthToRadiusOperation()
{
this->addInputSocket(DataType::Value);
@@ -113,3 +115,5 @@ void ConvertDepthToRadiusOperation::deinitExecution()
{
this->m_inputOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h
index 6a7cc4e004a..1f4e856b128 100644
--- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h
+++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h
@@ -21,6 +21,9 @@
#include "COM_FastGaussianBlurOperation.h"
#include "COM_NodeOperation.h"
#include "DNA_object_types.h"
+
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -81,3 +84,5 @@ class ConvertDepthToRadiusOperation : public NodeOperation {
this->m_blurPostOperation = operation;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc
index 648b3f0b30a..384936533c7 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertOperation.cc
@@ -18,8 +18,12 @@
#include "COM_ConvertOperation.h"
+#include "BLI_color.hh"
+
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
ConvertBaseOperation::ConvertBaseOperation()
{
this->m_inputOperation = nullptr;
@@ -353,21 +357,10 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4],
float y,
PixelSampler sampler)
{
- float inputValue[4];
- float alpha;
-
- this->m_inputOperation->readSampled(inputValue, x, y, sampler);
- alpha = inputValue[3];
-
- if (fabsf(alpha) < 1e-5f) {
- zero_v3(output);
- }
- else {
- mul_v3_v3fl(output, inputValue, 1.0f / alpha);
- }
-
- /* never touches the alpha */
- output[3] = alpha;
+ ColorSceneLinear4f<eAlpha::Premultiplied> input;
+ this->m_inputOperation->readSampled(input, x, y, sampler);
+ ColorSceneLinear4f<eAlpha::Straight> converted = input.unpremultiply_alpha();
+ copy_v4_v4(output, converted);
}
/* ******** Straight to Premul ******** */
@@ -383,16 +376,10 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4],
float y,
PixelSampler sampler)
{
- float inputValue[4];
- float alpha;
-
- this->m_inputOperation->readSampled(inputValue, x, y, sampler);
- alpha = inputValue[3];
-
- mul_v3_v3fl(output, inputValue, alpha);
-
- /* never touches the alpha */
- output[3] = alpha;
+ ColorSceneLinear4f<eAlpha::Straight> input;
+ this->m_inputOperation->readSampled(input, x, y, sampler);
+ ColorSceneLinear4f<eAlpha::Premultiplied> converted = input.premultiply_alpha();
+ copy_v4_v4(output, converted);
}
/* ******** Separate Channels ******** */
@@ -478,3 +465,5 @@ void CombineChannelsOperation::executePixelSampled(float output[4],
output[3] = input[0];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h
index 7e359a30d6c..7a726e35c7c 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.h
+++ b/source/blender/compositor/operations/COM_ConvertOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ConvertBaseOperation : public NodeOperation {
protected:
SocketReader *m_inputOperation;
@@ -182,3 +184,5 @@ class CombineChannelsOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc
index a5f2ae404e3..5ead300a368 100644
--- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc
@@ -19,10 +19,7 @@
#include "COM_ConvolutionEdgeFilterOperation.h"
#include "BLI_math.h"
-ConvolutionEdgeFilterOperation::ConvolutionEdgeFilterOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/)
{
@@ -97,3 +94,5 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y,
output[2] = MAX2(output[2], 0.0f);
output[3] = MAX2(output[3], 0.0f);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h
index 4ee72ffdd97..319b424bd4a 100644
--- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h
+++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h
@@ -20,8 +20,11 @@
#include "COM_ConvolutionFilterOperation.h"
+namespace blender::compositor {
+
class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation {
public:
- ConvolutionEdgeFilterOperation();
void executePixel(float output[4], int x, int y, void *data) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
index f80144fb06d..72cbbf4283a 100644
--- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
@@ -22,6 +22,8 @@
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
ConvolutionFilterOperation::ConvolutionFilterOperation()
{
this->addInputSocket(DataType::Color);
@@ -29,7 +31,7 @@ ConvolutionFilterOperation::ConvolutionFilterOperation()
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->m_inputOperation = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void ConvolutionFilterOperation::initExecution()
{
@@ -124,3 +126,5 @@ bool ConvolutionFilterOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h
index 08353fecca3..16dee502929 100644
--- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h
+++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ConvolutionFilterOperation : public NodeOperation {
private:
int m_filterWidth;
@@ -42,3 +44,5 @@ class ConvolutionFilterOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc
index 55a1e505ec8..f12d93bc8d3 100644
--- a/source/blender/compositor/operations/COM_CropOperation.cc
+++ b/source/blender/compositor/operations/COM_CropOperation.cc
@@ -19,9 +19,11 @@
#include "COM_CropOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
CropBaseOperation::CropBaseOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->m_inputOperation = nullptr;
this->m_settings = nullptr;
@@ -133,3 +135,5 @@ void CropImageOperation::executePixelSampled(float output[4],
zero_v4(output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h
index d2b9aee0bf3..acdff79a77c 100644
--- a/source/blender/compositor/operations/COM_CropOperation.h
+++ b/source/blender/compositor/operations/COM_CropOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class CropBaseOperation : public NodeOperation {
protected:
SocketReader *m_inputOperation;
@@ -64,3 +66,5 @@ class CropImageOperation : public CropBaseOperation {
unsigned int preferredResolution[2]) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
index 8d42a756f51..52ae1d6d5b5 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
@@ -18,14 +18,16 @@
#include "COM_CryptomatteOperation.h"
+namespace blender::compositor {
+
CryptomatteOperation::CryptomatteOperation(size_t num_inputs)
{
+ inputs.resize(num_inputs);
for (size_t i = 0; i < num_inputs; i++) {
this->addInputSocket(DataType::Color);
}
- inputs.resize(num_inputs);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
}
void CryptomatteOperation::initExecution()
@@ -38,7 +40,7 @@ void CryptomatteOperation::initExecution()
void CryptomatteOperation::addObjectIndex(float objectIndex)
{
if (objectIndex != 0.0f) {
- m_objectIndex.push_back(objectIndex);
+ m_objectIndex.append(objectIndex);
}
}
@@ -58,13 +60,15 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat
output[1] = ((float)((m3hash << 8)) / (float)UINT32_MAX);
output[2] = ((float)((m3hash << 16)) / (float)UINT32_MAX);
}
- for (size_t i = 0; i < m_objectIndex.size(); i++) {
- if (m_objectIndex[i] == input[0]) {
+ for (float hash : m_objectIndex) {
+ if (input[0] == hash) {
output[3] += input[1];
}
- if (m_objectIndex[i] == input[2]) {
+ if (input[2] == hash) {
output[3] += input[3];
}
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h
index a9cef7fe24b..1b91358d228 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.h
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h
@@ -20,12 +20,14 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class CryptomatteOperation : public NodeOperation {
private:
- std::vector<float> m_objectIndex;
+ Vector<float> m_objectIndex;
public:
- std::vector<SocketReader *> inputs;
+ Vector<SocketReader *> inputs;
CryptomatteOperation(size_t num_inputs = 6);
@@ -34,3 +36,5 @@ class CryptomatteOperation : public NodeOperation {
void addObjectIndex(float objectIndex);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc
index b58efcf0cca..8f655964570 100644
--- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc
@@ -20,6 +20,8 @@
#include "BKE_colortools.h"
+namespace blender::compositor {
+
CurveBaseOperation::CurveBaseOperation()
{
this->m_curveMapping = nullptr;
@@ -53,3 +55,5 @@ void CurveBaseOperation::setCurveMapping(CurveMapping *mapping)
}
this->m_curveMapping = BKE_curvemapping_copy(mapping);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h
index 15e484f4762..fff0f3168ba 100644
--- a/source/blender/compositor/operations/COM_CurveBaseOperation.h
+++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_color_types.h"
+namespace blender::compositor {
+
class CurveBaseOperation : public NodeOperation {
protected:
/**
@@ -40,3 +42,5 @@ class CurveBaseOperation : public NodeOperation {
void setCurveMapping(CurveMapping *mapping);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc
index 66d0f3fd9bd..ec11ad4d69a 100644
--- a/source/blender/compositor/operations/COM_DenoiseOperation.cc
+++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc
@@ -26,6 +26,8 @@ static pthread_mutex_t oidn_lock = BLI_MUTEX_INITIALIZER;
#endif
#include <iostream>
+namespace blender::compositor {
+
DenoiseOperation::DenoiseOperation()
{
this->addInputSocket(DataType::Color);
@@ -99,6 +101,11 @@ void DenoiseOperation::generateDenoise(float *data,
if (BLI_cpu_support_sse41())
# endif
{
+ /* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
+ * OpenImageDenoise is multithreaded internally and should use all available cores nonetheless.
+ */
+ BLI_mutex_lock(&oidn_lock);
+
oidn::DeviceRef device = oidn::newDevice();
device.commit();
@@ -143,10 +150,6 @@ void DenoiseOperation::generateDenoise(float *data,
}
filter.commit();
- /* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
- * OpenImageDenoise is multithreaded internally and should use all available cores nonetheless.
- */
- BLI_mutex_lock(&oidn_lock);
filter.execute();
BLI_mutex_unlock(&oidn_lock);
@@ -164,3 +167,5 @@ void DenoiseOperation::generateDenoise(float *data,
inputBufferColor,
sizeof(float[4]) * inputTileColor->getWidth() * inputTileColor->getHeight());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h
index 162fe70617f..a9298c17e92 100644
--- a/source/blender/compositor/operations/COM_DenoiseOperation.h
+++ b/source/blender/compositor/operations/COM_DenoiseOperation.h
@@ -21,6 +21,8 @@
#include "COM_SingleThreadedOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class DenoiseOperation : public SingleThreadedOperation {
private:
/**
@@ -64,3 +66,5 @@ class DenoiseOperation : public SingleThreadedOperation {
MemoryBuffer *createMemoryBuffer(rcti *rect) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc
index 813ae07a97a..fc8778c7d2e 100644
--- a/source/blender/compositor/operations/COM_DespeckleOperation.cc
+++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc
@@ -22,6 +22,8 @@
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
DespeckleOperation::DespeckleOperation()
{
this->addInputSocket(DataType::Color);
@@ -29,7 +31,7 @@ DespeckleOperation::DespeckleOperation()
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->m_inputOperation = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void DespeckleOperation::initExecution()
{
@@ -141,3 +143,5 @@ bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.h b/source/blender/compositor/operations/COM_DespeckleOperation.h
index dee355696af..e8d3461d2ec 100644
--- a/source/blender/compositor/operations/COM_DespeckleOperation.h
+++ b/source/blender/compositor/operations/COM_DespeckleOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DespeckleOperation : public NodeOperation {
private:
float m_threshold;
@@ -51,3 +53,5 @@ class DespeckleOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
index 1ce91aeb4c3..e380131634f 100644
--- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_DifferenceMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
DifferenceMatteOperation::DifferenceMatteOperation()
{
addInputSocket(DataType::Color);
@@ -83,3 +85,5 @@ void DifferenceMatteOperation::executePixelSampled(float output[4],
output[0] = inColor1[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
index ded819850f8..d3963fee1c1 100644
--- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -49,3 +51,5 @@ class DifferenceMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc
index 33ddf9187b0..9e18a8e2f2c 100644
--- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc
+++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc
@@ -22,12 +22,14 @@
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
// DilateErode Distance Threshold
DilateErodeThresholdOperation::DilateErodeThresholdOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
this->m_inset = 0.0f;
this->m__switch = 0.5f;
@@ -163,10 +165,10 @@ DilateDistanceOperation::DilateDistanceOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
- this->setComplex(true);
this->m_inputProgram = nullptr;
this->m_distance = 0.0f;
- this->setOpenCL(true);
+ flags.complex = true;
+ flags.open_cl = true;
}
void DilateDistanceOperation::initExecution()
{
@@ -321,7 +323,7 @@ DilateStepOperation::DilateStepOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
}
void DilateStepOperation::initExecution()
@@ -568,3 +570,5 @@ void *ErodeStepOperation::initializeTileData(rcti *rect)
return result;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.h b/source/blender/compositor/operations/COM_DilateErodeOperation.h
index 7497fb8ab08..a489e293e8e 100644
--- a/source/blender/compositor/operations/COM_DilateErodeOperation.h
+++ b/source/blender/compositor/operations/COM_DilateErodeOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DilateErodeThresholdOperation : public NodeOperation {
private:
/**
@@ -180,3 +182,5 @@ class ErodeStepOperation : public DilateStepOperation {
void *initializeTileData(rcti *rect) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
index 1a2701a681d..97bdc25af3b 100644
--- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
@@ -23,13 +23,14 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
DirectionalBlurOperation::DirectionalBlurOperation()
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
-
- this->setOpenCL(true);
+ flags.complex = true;
+ flags.open_cl = true;
this->m_inputProgram = nullptr;
}
@@ -66,7 +67,7 @@ void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void
const int iterations = pow(2.0f, this->m_data->iter);
float col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float col2[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- this->m_inputProgram->readSampled(col2, x, y, COM_PS_BILINEAR);
+ this->m_inputProgram->readSampled(col2, x, y, PixelSampler::Bilinear);
float ltx = this->m_tx;
float lty = this->m_ty;
float lsc = this->m_sc;
@@ -82,7 +83,7 @@ void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void
this->m_inputProgram->readSampled(col,
cs * u + ss * v + this->m_center_x_pix,
cs * v - ss * u + this->m_center_y_pix,
- COM_PS_BILINEAR);
+ PixelSampler::Bilinear);
add_v4_v4(col2, col);
@@ -144,3 +145,5 @@ bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
index daf6bc75254..5555520462b 100644
--- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
+++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper {
private:
SocketReader *m_inputProgram;
@@ -64,3 +66,5 @@ class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper
std::list<cl_mem> *clMemToCleanUp,
std::list<cl_kernel> *clKernelsToCleanUp) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc
index 12c7d29a210..9f3f5cfe489 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
DisplaceOperation::DisplaceOperation()
{
this->addInputSocket(DataType::Color);
@@ -27,7 +29,7 @@ DisplaceOperation::DisplaceOperation()
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputColorProgram = nullptr;
this->m_inputVectorProgram = nullptr;
@@ -56,7 +58,7 @@ void DisplaceOperation::executePixelSampled(float output[4],
pixelTransform(xy, uv, deriv);
if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) {
- this->m_inputColorProgram->readSampled(output, uv[0], uv[1], COM_PS_BILINEAR);
+ this->m_inputColorProgram->readSampled(output, uv[0], uv[1], PixelSampler::Bilinear);
}
else {
/* EWA filtering (without nearest it gets blurry with NO distortion) */
@@ -76,7 +78,7 @@ bool DisplaceOperation::read_displacement(
}
float col[4];
- m_inputVectorProgram->readSampled(col, x, y, COM_PS_BILINEAR);
+ m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear);
r_u = origin[0] - col[0] * xscale;
r_v = origin[1] - col[1] * yscale;
return true;
@@ -88,9 +90,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
float uv[2]; /* temporary variables for derivative estimation */
int num;
- m_inputScaleXProgram->readSampled(col, xy[0], xy[1], COM_PS_NEAREST);
+ m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
float xs = col[0];
- m_inputScaleYProgram->readSampled(col, xy[0], xy[1], COM_PS_NEAREST);
+ m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
float ys = col[0];
/* clamp x and y displacement to triple image resolution -
* to prevent hangs from huge values mistakenly plugged in eg. z buffers */
@@ -192,3 +194,5 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h
index 368ed29ff0b..fd82692f687 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.h
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DisplaceOperation : public NodeOperation {
private:
/**
@@ -64,3 +66,5 @@ class DisplaceOperation : public NodeOperation {
bool read_displacement(
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
index 9d00c2cb148..f4b77f5d32c 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
DisplaceSimpleOperation::DisplaceSimpleOperation()
{
this->addInputSocket(DataType::Color);
@@ -129,3 +131,5 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
index 63fe569c812..15e6fcd0523 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DisplaceSimpleOperation : public NodeOperation {
private:
/**
@@ -58,3 +60,5 @@ class DisplaceSimpleOperation : public NodeOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
index ae024d497d6..12cb7e7d075 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_DistanceRGBMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
DistanceRGBMatteOperation::DistanceRGBMatteOperation()
{
this->addInputSocket(DataType::Color);
@@ -90,3 +92,5 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4],
output[0] = inImage[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
index 0593dc6b976..6fe603233b7 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -51,3 +53,5 @@ class DistanceRGBMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
index f333cc1ecd9..597545dd706 100644
--- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
@@ -19,13 +19,12 @@
#include "COM_DistanceYCCMatteOperation.h"
#include "BLI_math.h"
-DistanceYCCMatteOperation::DistanceYCCMatteOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
float DistanceYCCMatteOperation::calculateDistance(float key[4], float image[4])
{
/* only measure the second 2 values */
return len_v2v2(key + 1, image + 1);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
index ce91e3a62ba..a87e885e5d8 100644
--- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
@@ -21,6 +21,8 @@
#include "COM_DistanceRGBMatteOperation.h"
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -28,10 +30,6 @@
class DistanceYCCMatteOperation : public DistanceRGBMatteOperation {
protected:
float calculateDistance(float key[4], float image[4]) override;
-
- public:
- /**
- * Default constructor
- */
- DistanceYCCMatteOperation();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc
index c5b89bb7fae..07075ae1d9d 100644
--- a/source/blender/compositor/operations/COM_DotproductOperation.cc
+++ b/source/blender/compositor/operations/COM_DotproductOperation.cc
@@ -18,6 +18,8 @@
#include "COM_DotproductOperation.h"
+namespace blender::compositor {
+
DotproductOperation::DotproductOperation()
{
this->addInputSocket(DataType::Vector);
@@ -52,3 +54,5 @@ void DotproductOperation::executePixelSampled(float output[4],
this->m_input2Operation->readSampled(input2, x, y, sampler);
output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DotproductOperation.h b/source/blender/compositor/operations/COM_DotproductOperation.h
index deff3ae5896..728033bcf32 100644
--- a/source/blender/compositor/operations/COM_DotproductOperation.h
+++ b/source/blender/compositor/operations/COM_DotproductOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DotproductOperation : public NodeOperation {
private:
SocketReader *m_input1Operation;
@@ -32,3 +34,5 @@ class DotproductOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
index 4c7cbb3cc7e..a3a86a6c502 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
@@ -23,6 +23,8 @@
#include "DNA_node_types.h"
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
// this part has been copied from the double edge mask
static void do_adjacentKeepBorders(unsigned int t,
unsigned int rw,
@@ -1315,7 +1317,7 @@ DoubleEdgeMaskOperation::DoubleEdgeMaskOperation()
this->m_inputOuterMask = nullptr;
this->m_adjacentOnly = false;
this->m_keepInside = false;
- this->setComplex(true);
+ this->flags.complex = true;
}
bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/,
@@ -1379,3 +1381,5 @@ void DoubleEdgeMaskOperation::deinitExecution()
this->m_cachedInstance = nullptr;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
index 228c8edc53e..e956e8edc3e 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DoubleEdgeMaskOperation : public NodeOperation {
private:
/**
@@ -65,3 +67,5 @@ class DoubleEdgeMaskOperation : public NodeOperation {
this->m_keepInside = keepInside;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
index 956b5e50edc..5a4503fecec 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
EllipseMaskOperation::EllipseMaskOperation()
{
this->addInputSocket(DataType::Value);
@@ -117,3 +119,5 @@ void EllipseMaskOperation::deinitExecution()
this->m_inputMask = nullptr;
this->m_inputValue = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h
index c7d7336600d..64afe0145cf 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class EllipseMaskOperation : public NodeOperation {
private:
/**
@@ -63,3 +65,5 @@ class EllipseMaskOperation : public NodeOperation {
this->m_maskType = maskType;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
index 4dded61fba5..2be6e4d1be7 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
@@ -22,6 +22,8 @@
#include "COM_FastGaussianBlurOperation.h"
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
FastGaussianBlurOperation::FastGaussianBlurOperation() : BlurBaseOperation(DataType::Color)
{
this->m_iirgaus = nullptr;
@@ -88,18 +90,18 @@ void *FastGaussianBlurOperation::initializeTileData(rcti *rect)
this->m_sy = this->m_data.sizey * this->m_size / 2.0f;
if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) {
- for (c = 0; c < COM_NUM_CHANNELS_COLOR; c++) {
+ for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
IIR_gauss(copy, this->m_sx, c, 3);
}
}
else {
if (this->m_sx > 0.0f) {
- for (c = 0; c < COM_NUM_CHANNELS_COLOR; c++) {
+ for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
IIR_gauss(copy, this->m_sx, c, 1);
}
}
if (this->m_sy > 0.0f) {
- for (c = 0; c < COM_NUM_CHANNELS_COLOR; c++) {
+ for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
IIR_gauss(copy, this->m_sy, c, 2);
}
}
@@ -264,7 +266,7 @@ FastGaussianBlurValueOperation::FastGaussianBlurValueOperation()
this->m_inputprogram = nullptr;
this->m_sigma = 1.0f;
this->m_overlay = 0;
- setComplex(true);
+ flags.complex = true;
}
void FastGaussianBlurValueOperation::executePixel(float output[4], int x, int y, void *data)
@@ -317,7 +319,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
float *src = newBuf->getBuffer();
float *dst = copy->getBuffer();
for (int i = copy->getWidth() * copy->getHeight(); i != 0;
- i--, src += COM_NUM_CHANNELS_VALUE, dst += COM_NUM_CHANNELS_VALUE) {
+ i--, src += COM_DATA_TYPE_VALUE_CHANNELS, dst += COM_DATA_TYPE_VALUE_CHANNELS) {
if (*src < *dst) {
*dst = *src;
}
@@ -327,7 +329,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
float *src = newBuf->getBuffer();
float *dst = copy->getBuffer();
for (int i = copy->getWidth() * copy->getHeight(); i != 0;
- i--, src += COM_NUM_CHANNELS_VALUE, dst += COM_NUM_CHANNELS_VALUE) {
+ i--, src += COM_DATA_TYPE_VALUE_CHANNELS, dst += COM_DATA_TYPE_VALUE_CHANNELS) {
if (*src > *dst) {
*dst = *src;
}
@@ -341,3 +343,5 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
unlockMutex();
return this->m_iirgaus;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
index c710baaaff8..c25afe6c4a4 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class FastGaussianBlurOperation : public BlurBaseOperation {
private:
float m_sx;
@@ -79,3 +81,5 @@ class FastGaussianBlurValueOperation : public NodeOperation {
this->m_overlay = overlay;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc
index 4837766b5eb..8afbec4ddbe 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.cc
+++ b/source/blender/compositor/operations/COM_FlipOperation.cc
@@ -18,6 +18,8 @@
#include "COM_FlipOperation.h"
+namespace blender::compositor {
+
FlipOperation::FlipOperation()
{
this->addInputSocket(DataType::Color);
@@ -72,3 +74,5 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h
index e571e57913f..f26d587fde6 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.h
+++ b/source/blender/compositor/operations/COM_FlipOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class FlipOperation : public NodeOperation {
private:
SocketReader *m_inputOperation;
@@ -44,3 +46,5 @@ class FlipOperation : public NodeOperation {
this->m_flipY = flipY;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
index 51c033498ef..16b79fddd06 100644
--- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
+++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GammaCorrectOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
GammaCorrectOperation::GammaCorrectOperation()
{
this->addInputSocket(DataType::Color);
@@ -102,3 +104,5 @@ void GammaUncorrectOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.h b/source/blender/compositor/operations/COM_GammaCorrectOperation.h
index 81724581f55..ac3d45b94b1 100644
--- a/source/blender/compositor/operations/COM_GammaCorrectOperation.h
+++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GammaCorrectOperation : public NodeOperation {
private:
/**
@@ -71,3 +73,5 @@ class GammaUncorrectOperation : public NodeOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc
index 327c5c24929..343e335070a 100644
--- a/source/blender/compositor/operations/COM_GammaOperation.cc
+++ b/source/blender/compositor/operations/COM_GammaOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GammaOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
GammaOperation::GammaOperation()
{
this->addInputSocket(DataType::Color);
@@ -54,3 +56,5 @@ void GammaOperation::deinitExecution()
this->m_inputProgram = nullptr;
this->m_inputGammaProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaOperation.h b/source/blender/compositor/operations/COM_GammaOperation.h
index c18f3bc3688..034046106d6 100644
--- a/source/blender/compositor/operations/COM_GammaOperation.h
+++ b/source/blender/compositor/operations/COM_GammaOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GammaOperation : public NodeOperation {
private:
/**
@@ -46,3 +48,5 @@ class GammaOperation : public NodeOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
index 1f03bb8d9cb..7ca5dc4ca76 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
@@ -22,6 +22,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() : BlurBaseOperation(DataType::Value)
{
this->m_gausstab = nullptr;
@@ -190,3 +192,5 @@ bool GaussianAlphaXBlurOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
index 51ca8359067..949956fae04 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GaussianAlphaXBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -65,3 +67,5 @@ class GaussianAlphaXBlurOperation : public BlurBaseOperation {
this->m_falloff = falloff;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
index de35c164fc7..d2385a972dd 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
@@ -22,6 +22,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() : BlurBaseOperation(DataType::Value)
{
this->m_gausstab = nullptr;
@@ -189,3 +191,5 @@ bool GaussianAlphaYBlurOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
index edd60acf2fc..d25770386c4 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GaussianAlphaYBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -65,3 +67,5 @@ class GaussianAlphaYBlurOperation : public BlurBaseOperation {
this->m_falloff = falloff;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
index 73b0914c086..b2c65ff2c96 100644
--- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
@@ -22,6 +22,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(DataType::Color)
{
this->m_gausstab = nullptr;
@@ -303,7 +305,7 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y,
int minyr = y - refrady < 0 ? -y : -refrady;
int maxyr = y + refrady > imgy ? imgy - y : refrady;
- float *srcd = buffer + COM_NUM_CHANNELS_COLOR * ((y + minyr) * imgx + x + minxr);
+ float *srcd = buffer + COM_DATA_TYPE_COLOR_CHANNELS * ((y + minyr) * imgx + x + minxr);
gausstabx = m_maintabs[refradx - 1];
gausstabcentx = gausstabx + refradx;
@@ -311,9 +313,9 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y,
gausstabcenty = gausstaby + refrady;
sum = gval = rval = bval = aval = 0.0f;
- for (i = minyr; i < maxyr; i++, srcd += COM_NUM_CHANNELS_COLOR * imgx) {
+ for (i = minyr; i < maxyr; i++, srcd += COM_DATA_TYPE_COLOR_CHANNELS * imgx) {
src = srcd;
- for (j = minxr; j < maxxr; j++, src += COM_NUM_CHANNELS_COLOR) {
+ for (j = minxr; j < maxxr; j++, src += COM_DATA_TYPE_COLOR_CHANNELS) {
val = gausstabcenty[i] * gausstabcentx[j];
sum += val;
@@ -360,3 +362,5 @@ bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest(
newInput.ymin = input->ymin - addy;
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
index 0eb8379dfc8..59ba3d06619 100644
--- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
class GaussianBokehBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -75,3 +77,5 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
index 43bf961cfc4..4b46cfc8776 100644
--- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
@@ -23,6 +23,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianXBlurOperation::GaussianXBlurOperation() : BlurBaseOperation(DataType::Color)
{
this->m_gausstab = nullptr;
@@ -205,3 +207,5 @@ bool GaussianXBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
index 44e6044e59b..15277f0a42d 100644
--- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GaussianXBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -62,6 +64,8 @@ class GaussianXBlurOperation : public BlurBaseOperation {
void checkOpenCL()
{
- this->setOpenCL(m_data.sizex >= 128);
+ flags.open_cl = (m_data.sizex >= 128);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
index 1e853dfb8f9..590ac5faa6a 100644
--- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
@@ -23,6 +23,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianYBlurOperation::GaussianYBlurOperation() : BlurBaseOperation(DataType::Color)
{
this->m_gausstab = nullptr;
@@ -205,3 +207,5 @@ bool GaussianYBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
index a0abb0dd050..56d40849ba4 100644
--- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GaussianYBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -62,6 +64,8 @@ class GaussianYBlurOperation : public BlurBaseOperation {
void checkOpenCL()
{
- this->setOpenCL(m_data.sizex >= 128);
+ flags.open_cl = (m_data.sizex >= 128);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc
index cdf64ed8b5a..90755d9f27a 100644
--- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GlareBaseOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
GlareBaseOperation::GlareBaseOperation()
{
this->addInputSocket(DataType::Color);
@@ -66,3 +68,5 @@ bool GlareBaseOperation::determineDependingAreaOfInterest(rcti * /*input*/,
newInput.ymin = 0;
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h
index 563877b734c..7ae15595e3b 100644
--- a/source/blender/compositor/operations/COM_GlareBaseOperation.h
+++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h
@@ -21,6 +21,8 @@
#include "COM_SingleThreadedOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/* utility functions used by glare, tonemap and lens distortion */
/* soms macros for color handling */
typedef float fRGB[4];
@@ -73,3 +75,5 @@ class GlareBaseOperation : public SingleThreadedOperation {
MemoryBuffer *createMemoryBuffer(rcti *rect) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
index 23dfc95f9e3..1c1eaebd331 100644
--- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GlareFogGlowOperation.h"
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
/*
* 2D Fast Hartley Transform, used for convolution
*/
@@ -271,7 +273,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
MemoryBuffer *rdst = new MemoryBuffer(DataType::Color, in1->get_rect());
memset(rdst->getBuffer(),
0,
- rdst->getWidth() * rdst->getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float));
+ rdst->getWidth() * rdst->getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float));
// convolution result width & height
w2 = 2 * kernelWidth - 1;
@@ -287,7 +289,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
// normalize convolutor
wt[0] = wt[1] = wt[2] = 0.0f;
for (y = 0; y < kernelHeight; y++) {
- colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < kernelWidth; x++) {
add_v3_v3(wt, colp[x]);
}
@@ -302,7 +304,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
wt[2] = 1.0f / wt[2];
}
for (y = 0; y < kernelHeight; y++) {
- colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < kernelWidth; x++) {
mul_v3_v3(colp[x], wt);
}
@@ -336,7 +338,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
// in2, channel ch -> data1
for (y = 0; y < kernelHeight; y++) {
fp = &data1ch[y * w2];
- colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < kernelWidth; x++) {
fp[x] = colp[x][ch];
}
@@ -351,7 +353,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
continue;
}
fp = &data2[y * w2];
- colp = (fRGB *)&imageBuffer[yy * imageWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&imageBuffer[yy * imageWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < xbsz; x++) {
int xx = xbl * xbsz + x;
if (xx >= imageWidth) {
@@ -381,7 +383,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
continue;
}
fp = &data2[y * w2];
- colp = (fRGB *)&rdst->getBuffer()[yy * imageWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&rdst->getBuffer()[yy * imageWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < (int)w2; x++) {
const int xx = xbl * xbsz + x - hw;
if ((xx < 0) || (xx >= imageWidth)) {
@@ -397,8 +399,9 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
MEM_freeN(data2);
MEM_freeN(data1);
- memcpy(
- dst, rdst->getBuffer(), sizeof(float) * imageWidth * imageHeight * COM_NUM_CHANNELS_COLOR);
+ memcpy(dst,
+ rdst->getBuffer(),
+ sizeof(float) * imageWidth * imageHeight * COM_DATA_TYPE_COLOR_CHANNELS);
delete (rdst);
}
@@ -442,3 +445,5 @@ void GlareFogGlowOperation::generateGlare(float *data,
convolve(data, inputTile, ckrn);
delete ckrn;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
index 844600527ee..5701f76ab13 100644
--- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
+++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class GlareFogGlowOperation : public GlareBaseOperation {
public:
GlareFogGlowOperation() : GlareBaseOperation()
@@ -31,3 +33,5 @@ class GlareFogGlowOperation : public GlareBaseOperation {
protected:
void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.cc b/source/blender/compositor/operations/COM_GlareGhostOperation.cc
index a4cd6dc60c1..22c8767632e 100644
--- a/source/blender/compositor/operations/COM_GlareGhostOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "COM_FastGaussianBlurOperation.h"
+namespace blender::compositor {
+
static float smoothMask(float x, float y)
{
float t;
@@ -123,7 +125,7 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No
memset(tbuf1.getBuffer(),
0,
- tbuf1.getWidth() * tbuf1.getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float));
+ tbuf1.getWidth() * tbuf1.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float));
for (n = 1; n < settings->iter && (!breaked); n++) {
for (y = 0; y < gbuf.getHeight() && (!breaked); y++) {
v = ((float)y + 0.5f) / (float)gbuf.getHeight();
@@ -147,9 +149,11 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No
}
memcpy(gbuf.getBuffer(),
tbuf1.getBuffer(),
- tbuf1.getWidth() * tbuf1.getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float));
+ tbuf1.getWidth() * tbuf1.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float));
}
memcpy(data,
gbuf.getBuffer(),
- gbuf.getWidth() * gbuf.getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float));
+ gbuf.getWidth() * gbuf.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.h b/source/blender/compositor/operations/COM_GlareGhostOperation.h
index 1b6bf3b2603..60256d8e0ef 100644
--- a/source/blender/compositor/operations/COM_GlareGhostOperation.h
+++ b/source/blender/compositor/operations/COM_GlareGhostOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class GlareGhostOperation : public GlareBaseOperation {
public:
GlareGhostOperation() : GlareBaseOperation()
@@ -31,3 +33,5 @@ class GlareGhostOperation : public GlareBaseOperation {
protected:
void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc
index a2cecb7e171..cc24a50a307 100644
--- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc
@@ -18,6 +18,8 @@
#include "COM_GlareSimpleStarOperation.h"
+namespace blender::compositor {
+
void GlareSimpleStarOperation::generateGlare(float *data,
MemoryBuffer *inputTile,
NodeGlare *settings)
@@ -97,3 +99,5 @@ void GlareSimpleStarOperation::generateGlare(float *data,
data[i] = tbuf1.getBuffer()[i] + tbuf2.getBuffer()[i];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
index ab92e0019fa..4a074f53e7b 100644
--- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
+++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class GlareSimpleStarOperation : public GlareBaseOperation {
public:
GlareSimpleStarOperation() : GlareBaseOperation()
@@ -31,3 +33,5 @@ class GlareSimpleStarOperation : public GlareBaseOperation {
protected:
void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc
index 0ea277881da..0af4eb43624 100644
--- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GlareStreaksOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
void GlareStreaksOperation::generateGlare(float *data,
MemoryBuffer *inputTile,
NodeGlare *settings)
@@ -97,3 +99,5 @@ void GlareStreaksOperation::generateGlare(float *data,
nump++;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.h b/source/blender/compositor/operations/COM_GlareStreaksOperation.h
index 833dbcfbb95..487c910960a 100644
--- a/source/blender/compositor/operations/COM_GlareStreaksOperation.h
+++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class GlareStreaksOperation : public GlareBaseOperation {
public:
GlareStreaksOperation() : GlareBaseOperation()
@@ -31,3 +33,5 @@ class GlareStreaksOperation : public GlareBaseOperation {
protected:
void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
index 984b433469a..1d3402f5b7b 100644
--- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
@@ -21,9 +21,11 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
GlareThresholdOperation::GlareThresholdOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_FIT);
+ this->addInputSocket(DataType::Color, ResizeMode::FitAny);
this->addOutputSocket(DataType::Color);
this->m_inputProgram = nullptr;
}
@@ -67,3 +69,5 @@ void GlareThresholdOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h
index 34df10a0f81..a6e971dada7 100644
--- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h
+++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_light_types.h"
+namespace blender::compositor {
+
class GlareThresholdOperation : public NodeOperation {
private:
/**
@@ -59,3 +61,5 @@ class GlareThresholdOperation : public NodeOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
index 0ef15b2b3d7..e341a88ff71 100644
--- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
+++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
@@ -22,6 +22,8 @@
#include "BKE_colortools.h"
+namespace blender::compositor {
+
HueSaturationValueCorrectOperation::HueSaturationValueCorrectOperation()
{
this->addInputSocket(DataType::Color);
@@ -70,3 +72,5 @@ void HueSaturationValueCorrectOperation::deinitExecution()
CurveBaseOperation::deinitExecution();
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
index 79eb40bbf60..703b2894bdb 100644
--- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
+++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
@@ -21,6 +21,8 @@
#include "COM_CurveBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class HueSaturationValueCorrectOperation : public CurveBaseOperation {
private:
/**
@@ -46,3 +48,5 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc
index 8757908e354..1bb247e9bc5 100644
--- a/source/blender/compositor/operations/COM_IDMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc
@@ -18,11 +18,13 @@
#include "COM_IDMaskOperation.h"
+namespace blender::compositor {
+
IDMaskOperation::IDMaskOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
- this->setComplex(true);
+ this->flags.complex = true;
}
void *IDMaskOperation::initializeTileData(rcti *rect)
@@ -39,3 +41,5 @@ void IDMaskOperation::executePixel(float output[4], int x, int y, void *data)
int buffer_index = (y * buffer_width + x);
output[0] = (roundf(buffer[buffer_index]) == this->m_objectIndex) ? 1.0f : 0.0f;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.h b/source/blender/compositor/operations/COM_IDMaskOperation.h
index 9a8d553b184..79b7e53b67c 100644
--- a/source/blender/compositor/operations/COM_IDMaskOperation.h
+++ b/source/blender/compositor/operations/COM_IDMaskOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class IDMaskOperation : public NodeOperation {
private:
float m_objectIndex;
@@ -35,3 +37,5 @@ class IDMaskOperation : public NodeOperation {
this->m_objectIndex = objectIndex;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc
index 06e8db7b467..a1d401d4499 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.cc
+++ b/source/blender/compositor/operations/COM_ImageOperation.cc
@@ -31,6 +31,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+namespace blender::compositor {
+
BaseImageOperation::BaseImageOperation()
{
this->m_image = nullptr;
@@ -123,13 +125,13 @@ static void sampleImageAtLocation(
{
if (ibuf->rect_float) {
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, nullptr, color, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, nullptr, color, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, nullptr, color, x, y);
break;
}
@@ -137,13 +139,13 @@ static void sampleImageAtLocation(
else {
unsigned char byte_color[4];
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, byte_color, nullptr, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, byte_color, nullptr, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, byte_color, nullptr, x, y);
break;
}
@@ -203,3 +205,5 @@ void ImageDepthOperation::executePixelSampled(float output[4],
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h
index 0bb92ce1b7e..58373663db5 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.h
+++ b/source/blender/compositor/operations/COM_ImageOperation.h
@@ -27,6 +27,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+namespace blender::compositor {
+
/**
* \brief Base class for all image operations
*/
@@ -102,3 +104,5 @@ class ImageDepthOperation : public BaseImageOperation {
ImageDepthOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc
index f4e38c85e50..413ed2694a9 100644
--- a/source/blender/compositor/operations/COM_InpaintOperation.cc
+++ b/source/blender/compositor/operations/COM_InpaintOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
#define ASSERT_XY_RANGE(x, y) \
BLI_assert(x >= 0 && x < this->getWidth() && y >= 0 && y < this->getHeight())
@@ -31,7 +33,7 @@ InpaintSimpleOperation::InpaintSimpleOperation()
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputImageProgram = nullptr;
this->m_pixelorder = nullptr;
this->m_manhattan_distance = nullptr;
@@ -76,7 +78,8 @@ float *InpaintSimpleOperation::get_pixel(int x, int y)
ASSERT_XY_RANGE(x, y);
- return &this->m_cached_buffer[y * width * COM_NUM_CHANNELS_COLOR + x * COM_NUM_CHANNELS_COLOR];
+ return &this->m_cached_buffer[y * width * COM_DATA_TYPE_COLOR_CHANNELS +
+ x * COM_DATA_TYPE_COLOR_CHANNELS];
}
int InpaintSimpleOperation::mdist(int x, int y)
@@ -282,3 +285,5 @@ bool InpaintSimpleOperation::determineDependingAreaOfInterest(rcti * /*input*/,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h
index 8fefa475b4a..e3d27bf7704 100644
--- a/source/blender/compositor/operations/COM_InpaintOperation.h
+++ b/source/blender/compositor/operations/COM_InpaintOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class InpaintSimpleOperation : public NodeOperation {
protected:
/**
@@ -72,3 +74,5 @@ class InpaintSimpleOperation : public NodeOperation {
bool next_pixel(int &x, int &y, int &curr, int iters);
void pix_step(int x, int y);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc
index 53e32baaa3d..339e40a5d1f 100644
--- a/source/blender/compositor/operations/COM_InvertOperation.cc
+++ b/source/blender/compositor/operations/COM_InvertOperation.cc
@@ -18,6 +18,8 @@
#include "COM_InvertOperation.h"
+namespace blender::compositor {
+
InvertOperation::InvertOperation()
{
this->addInputSocket(DataType::Value);
@@ -67,3 +69,5 @@ void InvertOperation::deinitExecution()
this->m_inputValueProgram = nullptr;
this->m_inputColorProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h
index a7b0f823ae2..17e5eb95f3e 100644
--- a/source/blender/compositor/operations/COM_InvertOperation.h
+++ b/source/blender/compositor/operations/COM_InvertOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class InvertOperation : public NodeOperation {
private:
/**
@@ -58,3 +60,5 @@ class InvertOperation : public NodeOperation {
this->m_alpha = alpha;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
index 0d9613eea96..994b00cd3f4 100644
--- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
KeyingBlurOperation::KeyingBlurOperation()
{
this->addInputSocket(DataType::Value);
@@ -31,7 +33,7 @@ KeyingBlurOperation::KeyingBlurOperation()
this->m_size = 0;
this->m_axis = BLUR_AXIS_X;
- this->setComplex(true);
+ this->flags.complex = true;
}
void *KeyingBlurOperation::initializeTileData(rcti *rect)
@@ -93,3 +95,5 @@ bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h
index b1ea4229b6d..b055d7713f1 100644
--- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* Class with implementation of blurring for keying node
*/
@@ -53,3 +55,5 @@ class KeyingBlurOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc
index e8556d9d8c9..4029be4e077 100644
--- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
KeyingClipOperation::KeyingClipOperation()
{
this->addInputSocket(DataType::Value);
@@ -36,7 +38,7 @@ KeyingClipOperation::KeyingClipOperation()
this->m_isEdgeMatte = false;
- this->setComplex(true);
+ this->flags.complex = true;
}
void *KeyingClipOperation::initializeTileData(rcti *rect)
@@ -127,3 +129,5 @@ bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h
index 1b2fa886814..0a21fb48c99 100644
--- a/source/blender/compositor/operations/COM_KeyingClipOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* Class with implementation of black/white clipping for keying node
*/
@@ -67,3 +69,5 @@ class KeyingClipOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
index 5caa450a878..d31a88cb91e 100644
--- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
KeyingDespillOperation::KeyingDespillOperation()
{
this->addInputSocket(DataType::Color);
@@ -79,3 +81,5 @@ void KeyingDespillOperation::executePixelSampled(float output[4],
output[screen_primary_channel] = pixelColor[screen_primary_channel] - amount_despill;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h
index c201388c1c1..279ac60e6e9 100644
--- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* Class with implementation of keying despill node
*/
@@ -47,3 +49,5 @@ class KeyingDespillOperation : public NodeOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc
index 108b7c60874..e786e4b8219 100644
--- a/source/blender/compositor/operations/COM_KeyingOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
static float get_pixel_saturation(const float pixelColor[4],
float screen_balance,
int primary_channel)
@@ -107,3 +109,5 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h
index 13f4e5e247b..3d41ecaa0f6 100644
--- a/source/blender/compositor/operations/COM_KeyingOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingOperation.h
@@ -24,6 +24,8 @@
#include "BLI_listbase.h"
+namespace blender::compositor {
+
/**
* Class with implementation of keying node
*/
@@ -47,3 +49,5 @@ class KeyingOperation : public NodeOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
index 801750d99d0..17b613246ad 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
@@ -30,13 +30,15 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
KeyingScreenOperation::KeyingScreenOperation()
{
this->addOutputSocket(DataType::Color);
this->m_movieClip = nullptr;
this->m_framenumber = 0;
this->m_trackingObject[0] = 0;
- setComplex(true);
+ flags.complex = true;
}
void KeyingScreenOperation::initExecution()
@@ -344,3 +346,5 @@ void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *da
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
index 86cd94e308d..4118d229be9 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
@@ -29,6 +29,8 @@
#include "BLI_voronoi_2d.h"
+namespace blender::compositor {
+
/**
* Class with implementation of green screen gradient rasterization
*/
@@ -83,3 +85,5 @@ class KeyingScreenOperation : public NodeOperation {
void executePixel(float output[4], int x, int y, void *data) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
index 43cc8f8bf71..0afc4278a45 100644
--- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
@@ -21,6 +21,8 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
LuminanceMatteOperation::LuminanceMatteOperation()
{
addInputSocket(DataType::Color);
@@ -75,3 +77,5 @@ void LuminanceMatteOperation::executePixelSampled(float output[4],
/* don't make something that was more transparent less transparent */
output[0] = min_ff(alpha, inColor[3]);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
index 4e716468568..035c68b9d59 100644
--- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
+++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -48,3 +50,5 @@ class LuminanceMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc
index 78ba446051e..ada3cd6f159 100644
--- a/source/blender/compositor/operations/COM_MapRangeOperation.cc
+++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc
@@ -18,6 +18,8 @@
#include "COM_MapRangeOperation.h"
+namespace blender::compositor {
+
MapRangeOperation::MapRangeOperation()
{
this->addInputSocket(DataType::Value);
@@ -101,3 +103,5 @@ void MapRangeOperation::deinitExecution()
this->m_destMinOperation = nullptr;
this->m_destMaxOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.h b/source/blender/compositor/operations/COM_MapRangeOperation.h
index 27a7cfd90c3..a544c59887e 100644
--- a/source/blender/compositor/operations/COM_MapRangeOperation.h
+++ b/source/blender/compositor/operations/COM_MapRangeOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_texture_types.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -67,3 +69,5 @@ class MapRangeOperation : public NodeOperation {
this->m_useClamp = value;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc
index 7328de7f49f..74e3d965d41 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.cc
+++ b/source/blender/compositor/operations/COM_MapUVOperation.cc
@@ -19,13 +19,15 @@
#include "COM_MapUVOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
MapUVOperation::MapUVOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Vector);
this->addOutputSocket(DataType::Color);
this->m_alpha = 0.0f;
- this->setComplex(true);
+ this->flags.complex = true;
setResolutionInputSocketIndex(1);
this->m_inputUVProgram = nullptr;
@@ -89,7 +91,7 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_
}
float vector[3];
- m_inputUVProgram->readSampled(vector, x, y, COM_PS_BILINEAR);
+ m_inputUVProgram->readSampled(vector, x, y, PixelSampler::Bilinear);
r_u = vector[0] * m_inputColorProgram->getWidth();
r_v = vector[1] * m_inputColorProgram->getHeight();
r_alpha = vector[2];
@@ -183,3 +185,5 @@ bool MapUVOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h
index e3481b9cae9..eb5f7d49122 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.h
+++ b/source/blender/compositor/operations/COM_MapUVOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class MapUVOperation : public NodeOperation {
private:
/**
@@ -65,3 +67,5 @@ class MapUVOperation : public NodeOperation {
private:
bool read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc
index 9c28e3c2577..03fa80d220d 100644
--- a/source/blender/compositor/operations/COM_MapValueOperation.cc
+++ b/source/blender/compositor/operations/COM_MapValueOperation.cc
@@ -18,6 +18,8 @@
#include "COM_MapValueOperation.h"
+namespace blender::compositor {
+
MapValueOperation::MapValueOperation()
{
this->addInputSocket(DataType::Value);
@@ -57,3 +59,5 @@ void MapValueOperation::deinitExecution()
{
this->m_inputOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h
index 98417001f4a..eb7714714e9 100644
--- a/source/blender/compositor/operations/COM_MapValueOperation.h
+++ b/source/blender/compositor/operations/COM_MapValueOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_texture_types.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -62,3 +64,5 @@ class MapValueOperation : public NodeOperation {
this->m_settings = settings;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc
index 64d8c9a5492..c7763f08e71 100644
--- a/source/blender/compositor/operations/COM_MaskOperation.cc
+++ b/source/blender/compositor/operations/COM_MaskOperation.cc
@@ -26,6 +26,8 @@
#include "BKE_lib_id.h"
#include "BKE_mask.h"
+namespace blender::compositor {
+
MaskOperation::MaskOperation()
{
this->addOutputSocket(DataType::Value);
@@ -158,3 +160,5 @@ void MaskOperation::executePixelSampled(float output[4],
output[0] /= this->m_rasterMaskHandleTot;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h
index 909af921625..48fd54b00fe 100644
--- a/source/blender/compositor/operations/COM_MaskOperation.h
+++ b/source/blender/compositor/operations/COM_MaskOperation.h
@@ -23,6 +23,11 @@
#include "DNA_mask_types.h"
#include "IMB_imbuf_types.h"
+/* Forward declarations. */
+struct MaskRasterHandle;
+
+namespace blender::compositor {
+
/**
* Class with implementation of mask rasterization
*/
@@ -94,3 +99,5 @@ class MaskOperation : public NodeOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc
index 57ccbe7792b..a94c14347fb 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
MathBaseOperation::MathBaseOperation()
{
this->addInputSocket(DataType::Value);
@@ -748,3 +750,5 @@ void MathSmoothMaxOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h
index 27a01667da5..69555524274 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.h
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -368,3 +370,5 @@ class MathSmoothMaxOperation : public MathBaseOperation {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc
index e70c59c6a01..58fa09fa2a8 100644
--- a/source/blender/compositor/operations/COM_MixOperation.cc
+++ b/source/blender/compositor/operations/COM_MixOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
/* ******** Mix Base Operation ******** */
MixBaseOperation::MixBaseOperation()
@@ -97,11 +99,6 @@ void MixBaseOperation::deinitExecution()
/* ******** Mix Add Operation ******** */
-MixAddOperation::MixAddOperation()
-{
- /* pass */
-}
-
void MixAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputColor1[4];
@@ -126,11 +123,6 @@ void MixAddOperation::executePixelSampled(float output[4], float x, float y, Pix
/* ******** Mix Blend Operation ******** */
-MixBlendOperation::MixBlendOperation()
-{
- /* pass */
-}
-
void MixBlendOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -160,11 +152,6 @@ void MixBlendOperation::executePixelSampled(float output[4],
/* ******** Mix Burn Operation ******** */
-MixColorBurnOperation::MixColorBurnOperation()
-{
- /* pass */
-}
-
void MixColorBurnOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -243,11 +230,6 @@ void MixColorBurnOperation::executePixelSampled(float output[4],
/* ******** Mix Color Operation ******** */
-MixColorOperation::MixColorOperation()
-{
- /* pass */
-}
-
void MixColorOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -288,11 +270,6 @@ void MixColorOperation::executePixelSampled(float output[4],
/* ******** Mix Darken Operation ******** */
-MixDarkenOperation::MixDarkenOperation()
-{
- /* pass */
-}
-
void MixDarkenOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -321,11 +298,6 @@ void MixDarkenOperation::executePixelSampled(float output[4],
/* ******** Mix Difference Operation ******** */
-MixDifferenceOperation::MixDifferenceOperation()
-{
- /* pass */
-}
-
void MixDifferenceOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -354,11 +326,6 @@ void MixDifferenceOperation::executePixelSampled(float output[4],
/* ******** Mix Difference Operation ******** */
-MixDivideOperation::MixDivideOperation()
-{
- /* pass */
-}
-
void MixDivideOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -404,11 +371,6 @@ void MixDivideOperation::executePixelSampled(float output[4],
/* ******** Mix Dodge Operation ******** */
-MixDodgeOperation::MixDodgeOperation()
-{
- /* pass */
-}
-
void MixDodgeOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -492,11 +454,6 @@ void MixDodgeOperation::executePixelSampled(float output[4],
/* ******** Mix Glare Operation ******** */
-MixGlareOperation::MixGlareOperation()
-{
- /* pass */
-}
-
void MixGlareOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -505,27 +462,26 @@ void MixGlareOperation::executePixelSampled(float output[4],
float inputColor1[4];
float inputColor2[4];
float inputValue[4];
- float value;
+ float value, input_weight, glare_weight;
this->m_inputValueOperation->readSampled(inputValue, x, y, sampler);
this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler);
this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler);
value = inputValue[0];
- float mf = 2.0f - 2.0f * fabsf(value - 0.5f);
-
- if (inputColor1[0] < 0.0f) {
- inputColor1[0] = 0.0f;
+ /* Linear interpolation between 3 cases:
+ * value=-1:output=input value=0:output=input+glare value=1:output=glare
+ */
+ if (value < 0.0f) {
+ input_weight = 1.0f;
+ glare_weight = 1.0f + value;
}
- if (inputColor1[1] < 0.0f) {
- inputColor1[1] = 0.0f;
- }
- if (inputColor1[2] < 0.0f) {
- inputColor1[2] = 0.0f;
+ else {
+ input_weight = 1.0f - value;
+ glare_weight = 1.0f;
}
-
- output[0] = mf * MAX2(inputColor1[0] + value * (inputColor2[0] - inputColor1[0]), 0.0f);
- output[1] = mf * MAX2(inputColor1[1] + value * (inputColor2[1] - inputColor1[1]), 0.0f);
- output[2] = mf * MAX2(inputColor1[2] + value * (inputColor2[2] - inputColor1[2]), 0.0f);
+ output[0] = input_weight * MAX2(inputColor1[0], 0.0f) + glare_weight * inputColor2[0];
+ output[1] = input_weight * MAX2(inputColor1[1], 0.0f) + glare_weight * inputColor2[1];
+ output[2] = input_weight * MAX2(inputColor1[2], 0.0f) + glare_weight * inputColor2[2];
output[3] = inputColor1[3];
clampIfNeeded(output);
@@ -533,11 +489,6 @@ void MixGlareOperation::executePixelSampled(float output[4],
/* ******** Mix Hue Operation ******** */
-MixHueOperation::MixHueOperation()
-{
- /* pass */
-}
-
void MixHueOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputColor1[4];
@@ -575,11 +526,6 @@ void MixHueOperation::executePixelSampled(float output[4], float x, float y, Pix
/* ******** Mix Lighten Operation ******** */
-MixLightenOperation::MixLightenOperation()
-{
- /* pass */
-}
-
void MixLightenOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -626,11 +572,6 @@ void MixLightenOperation::executePixelSampled(float output[4],
/* ******** Mix Linear Light Operation ******** */
-MixLinearLightOperation::MixLinearLightOperation()
-{
- /* pass */
-}
-
void MixLinearLightOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -674,11 +615,6 @@ void MixLinearLightOperation::executePixelSampled(float output[4],
/* ******** Mix Multiply Operation ******** */
-MixMultiplyOperation::MixMultiplyOperation()
-{
- /* pass */
-}
-
void MixMultiplyOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -707,11 +643,6 @@ void MixMultiplyOperation::executePixelSampled(float output[4],
/* ******** Mix Overlay Operation ******** */
-MixOverlayOperation::MixOverlayOperation()
-{
- /* pass */
-}
-
void MixOverlayOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -757,11 +688,6 @@ void MixOverlayOperation::executePixelSampled(float output[4],
/* ******** Mix Saturation Operation ******** */
-MixSaturationOperation::MixSaturationOperation()
-{
- /* pass */
-}
-
void MixSaturationOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -799,11 +725,6 @@ void MixSaturationOperation::executePixelSampled(float output[4],
/* ******** Mix Screen Operation ******** */
-MixScreenOperation::MixScreenOperation()
-{
- /* pass */
-}
-
void MixScreenOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -833,11 +754,6 @@ void MixScreenOperation::executePixelSampled(float output[4],
/* ******** Mix Soft Light Operation ******** */
-MixSoftLightOperation::MixSoftLightOperation()
-{
- /* pass */
-}
-
void MixSoftLightOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -879,11 +795,6 @@ void MixSoftLightOperation::executePixelSampled(float output[4],
/* ******** Mix Subtract Operation ******** */
-MixSubtractOperation::MixSubtractOperation()
-{
- /* pass */
-}
-
void MixSubtractOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -911,11 +822,6 @@ void MixSubtractOperation::executePixelSampled(float output[4],
/* ******** Mix Value Operation ******** */
-MixValueOperation::MixValueOperation()
-{
- /* pass */
-}
-
void MixValueOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -944,3 +850,5 @@ void MixValueOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h
index dd1f34a5322..6c241bc5762 100644
--- a/source/blender/compositor/operations/COM_MixOperation.h
+++ b/source/blender/compositor/operations/COM_MixOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* All this programs converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -83,114 +85,97 @@ class MixBaseOperation : public NodeOperation {
class MixAddOperation : public MixBaseOperation {
public:
- MixAddOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixBlendOperation : public MixBaseOperation {
public:
- MixBlendOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixColorBurnOperation : public MixBaseOperation {
public:
- MixColorBurnOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixColorOperation : public MixBaseOperation {
public:
- MixColorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDarkenOperation : public MixBaseOperation {
public:
- MixDarkenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDifferenceOperation : public MixBaseOperation {
public:
- MixDifferenceOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDivideOperation : public MixBaseOperation {
public:
- MixDivideOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDodgeOperation : public MixBaseOperation {
public:
- MixDodgeOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixGlareOperation : public MixBaseOperation {
public:
- MixGlareOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixHueOperation : public MixBaseOperation {
public:
- MixHueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixLightenOperation : public MixBaseOperation {
public:
- MixLightenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixLinearLightOperation : public MixBaseOperation {
public:
- MixLinearLightOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixMultiplyOperation : public MixBaseOperation {
public:
- MixMultiplyOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixOverlayOperation : public MixBaseOperation {
public:
- MixOverlayOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSaturationOperation : public MixBaseOperation {
public:
- MixSaturationOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixScreenOperation : public MixBaseOperation {
public:
- MixScreenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSoftLightOperation : public MixBaseOperation {
public:
- MixSoftLightOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSubtractOperation : public MixBaseOperation {
public:
- MixSubtractOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixValueOperation : public MixBaseOperation {
public:
- MixValueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
index 3920b4c02bd..a9f187258b2 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
@@ -21,6 +21,8 @@
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
MovieClipAttributeOperation::MovieClipAttributeOperation()
{
this->addOutputSocket(DataType::Value);
@@ -80,3 +82,5 @@ void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2]
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
index 6a6920247e6..8507e98d08f 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_movieclip_types.h"
+namespace blender::compositor {
+
typedef enum MovieClipAttribute {
MCA_SCALE,
MCA_X,
@@ -71,3 +73,5 @@ class MovieClipAttributeOperation : public NodeOperation {
this->m_invert = invert;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc
index 5d5880e14b0..d93a75407c4 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc
@@ -26,6 +26,8 @@
#include "IMB_imbuf.h"
+namespace blender::compositor {
+
MovieClipBaseOperation::MovieClipBaseOperation()
{
this->m_movieClip = nullptr;
@@ -101,13 +103,13 @@ void MovieClipBaseOperation::executePixelSampled(float output[4],
}
else {
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, nullptr, output, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, nullptr, output, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, nullptr, output, x, y);
break;
}
@@ -133,3 +135,5 @@ void MovieClipAlphaOperation::executePixelSampled(float output[4],
MovieClipBaseOperation::executePixelSampled(result, x, y, sampler);
output[0] = result[3];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h
index 78cd1cc0c90..c853ea43762 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.h
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.h
@@ -23,6 +23,8 @@
#include "DNA_movieclip_types.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
/**
* Base class for movie clip
*/
@@ -77,3 +79,5 @@ class MovieClipAlphaOperation : public MovieClipBaseOperation {
MovieClipAlphaOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
index fcd482a69ef..c8e045ea117 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_linklist.h"
+namespace blender::compositor {
+
MovieDistortionOperation::MovieDistortionOperation(bool distortion)
{
this->addInputSocket(DataType::Color);
@@ -107,10 +109,10 @@ void MovieDistortionOperation::executePixelSampled(float output[4],
float u = out[0] * aspx /* + 0.5 * overscan * w */,
v = (out[1] * aspy /* + 0.5 * overscan * h */) * pixel_aspect;
- this->m_inputOperation->readSampled(output, u, v, COM_PS_BILINEAR);
+ this->m_inputOperation->readSampled(output, u, v, PixelSampler::Bilinear);
}
else {
- this->m_inputOperation->readSampled(output, x, y, COM_PS_BILINEAR);
+ this->m_inputOperation->readSampled(output, x, y, PixelSampler::Bilinear);
}
}
@@ -125,3 +127,5 @@ bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input,
newInput.ymax = input->ymax + m_margin[1];
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
index ef9d6be697b..631a62f7ebf 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
@@ -24,6 +24,8 @@
#include "BKE_tracking.h"
+namespace blender::compositor {
+
class MovieDistortionOperation : public NodeOperation {
private:
SocketReader *m_inputOperation;
@@ -57,3 +59,5 @@ class MovieDistortionOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc
index 60936ee1939..647e93225e5 100644
--- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc
+++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc
@@ -21,6 +21,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
MultilayerBaseOperation::MultilayerBaseOperation(RenderLayer *render_layer,
RenderPass *render_pass,
int view)
@@ -49,7 +51,7 @@ ImBuf *MultilayerBaseOperation::getImBuf()
return nullptr;
}
-std::unique_ptr<MetaData> MultilayerColorOperation::getMetaData() const
+std::unique_ptr<MetaData> MultilayerColorOperation::getMetaData()
{
BLI_assert(this->m_buffer);
MetaDataExtractCallbackData callback_data = {nullptr};
@@ -86,13 +88,13 @@ void MultilayerColorOperation::executePixelSampled(float output[4],
else {
if (this->m_numberOfChannels == 4) {
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
nearest_interpolation_color(this->m_buffer, nullptr, output, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
bilinear_interpolation_color(this->m_buffer, nullptr, output, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
bicubic_interpolation_color(this->m_buffer, nullptr, output, x, y);
break;
}
@@ -155,3 +157,5 @@ void MultilayerVectorOperation::executePixelSampled(float output[4],
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h
index dceb57de140..6e6062cf854 100644
--- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h
+++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h
@@ -20,6 +20,8 @@
#include "COM_ImageOperation.h"
+namespace blender::compositor {
+
class MultilayerBaseOperation : public BaseImageOperation {
private:
int m_passId;
@@ -45,7 +47,7 @@ class MultilayerColorOperation : public MultilayerBaseOperation {
this->addOutputSocket(DataType::Color);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- std::unique_ptr<MetaData> getMetaData() const override;
+ std::unique_ptr<MetaData> getMetaData() override;
};
class MultilayerValueOperation : public MultilayerBaseOperation {
@@ -67,3 +69,5 @@ class MultilayerVectorOperation : public MultilayerBaseOperation {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc
index fd3b951c842..faacb429f71 100644
--- a/source/blender/compositor/operations/COM_NormalizeOperation.cc
+++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc
@@ -18,13 +18,15 @@
#include "COM_NormalizeOperation.h"
+namespace blender::compositor {
+
NormalizeOperation::NormalizeOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
this->m_imageReader = nullptr;
this->m_cachedInstance = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void NormalizeOperation::initExecution()
{
@@ -124,3 +126,5 @@ void NormalizeOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/)
{
/* pass */
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.h b/source/blender/compositor/operations/COM_NormalizeOperation.h
index 6447aa930f1..93d4a0fc67d 100644
--- a/source/blender/compositor/operations/COM_NormalizeOperation.h
+++ b/source/blender/compositor/operations/COM_NormalizeOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief base class of normalize, implementing the simple normalize
* \ingroup operation
@@ -63,3 +65,5 @@ class NormalizeOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
index 7044fe402eb..5b6f650d40e 100644
--- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
@@ -37,6 +37,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
/************************************ OpenEXR Singlelayer Multiview ******************************/
OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOperation(
@@ -380,3 +382,5 @@ void OutputStereoOperation::deinitExecution()
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
index 90c683a57b5..6230a6f306b 100644
--- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
@@ -28,6 +28,8 @@
#include "intern/openexr/openexr_multi.h"
+namespace blender::compositor {
+
class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOperation {
private:
public:
@@ -80,3 +82,5 @@ class OutputStereoOperation : public OutputSingleLayerOperation {
void *get_handle(const char *filename);
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc
index a6135ef064c..1ee749b1a49 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc
@@ -40,6 +40,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
void add_exr_channels(void *exrhandle,
const char *layerName,
const DataType datatype,
@@ -191,7 +193,7 @@ static void write_buffer_rect(rcti *rect,
for (y = y1; y < y2 && (!breaked); y++) {
for (x = x1; x < x2 && (!breaked); x++) {
- reader->readSampled(color, x, y, COM_PS_NEAREST);
+ reader->readSampled(color, x, y, PixelSampler::Nearest);
for (i = 0; i < size; i++) {
buffer[offset + i] = color[i];
@@ -321,6 +323,7 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *
this->m_exr_codec = exr_codec;
this->m_exr_half_float = exr_half_float;
this->m_viewName = viewName;
+ this->setResolutionInputSocketIndex(RESOLUTION_INPUT_ANY);
}
void OutputOpenExrMultiLayerOperation::add_layer(const char *name,
@@ -328,7 +331,7 @@ void OutputOpenExrMultiLayerOperation::add_layer(const char *name,
bool use_layer)
{
this->addInputSocket(datatype);
- this->m_layers.push_back(OutputOpenExrLayer(name, datatype, use_layer));
+ this->m_layers.append(OutputOpenExrLayer(name, datatype, use_layer));
}
StampData *OutputOpenExrMultiLayerOperation::createStampData() const
@@ -338,17 +341,16 @@ StampData *OutputOpenExrMultiLayerOperation::createStampData() const
RenderResult render_result;
StampData *stamp_data = BKE_stamp_info_from_scene_static(m_scene);
render_result.stamp_data = stamp_data;
- for (int i = 0; i < this->m_layers.size(); i++) {
- const OutputOpenExrLayer *layer = &this->m_layers[i];
+ for (const OutputOpenExrLayer &layer : m_layers) {
/* Skip unconnected sockets. */
- if (layer->imageInput == nullptr) {
+ if (layer.imageInput == nullptr) {
continue;
}
- std::unique_ptr<MetaData> meta_data = layer->imageInput->getMetaData();
+ std::unique_ptr<MetaData> meta_data = layer.imageInput->getMetaData();
if (meta_data) {
blender::StringRef layer_name =
blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(
- blender::StringRef(layer->name, BLI_strnlen(layer->name, sizeof(layer->name))));
+ blender::StringRef(layer.name, BLI_strnlen(layer.name, sizeof(layer.name))));
meta_data->replaceHashNeutralCryptomatteKeys(layer_name);
meta_data->addToRenderResult(&render_result);
}
@@ -441,3 +443,5 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
BKE_stamp_data_free(stamp_data);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h
index aa7ba35ddee..64ab4c06e7c 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.h
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.h
@@ -27,6 +27,8 @@
#include "intern/openexr/openexr_multi.h"
+namespace blender::compositor {
+
/* Writes the image to a single-layer file. */
class OutputSingleLayerOperation : public NodeOperation {
protected:
@@ -64,14 +66,9 @@ class OutputSingleLayerOperation : public NodeOperation {
}
void initExecution() override;
void deinitExecution() override;
- CompositorPriority getRenderPriority() const override
- {
- return CompositorPriority::Low;
- }
-
- bool isFileOutputOperation() const override
+ eCompositorPriority getRenderPriority() const override
{
- return true;
+ return eCompositorPriority::Low;
}
};
@@ -91,8 +88,6 @@ struct OutputOpenExrLayer {
/* Writes inputs into OpenEXR multilayer channels. */
class OutputOpenExrMultiLayerOperation : public NodeOperation {
protected:
- typedef std::vector<OutputOpenExrLayer> LayerList;
-
const Scene *m_scene;
const RenderData *m_rd;
const bNodeTree *m_tree;
@@ -100,7 +95,7 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
char m_path[FILE_MAX];
char m_exr_codec;
bool m_exr_half_float;
- LayerList m_layers;
+ Vector<OutputOpenExrLayer> m_layers;
const char *m_viewName;
StampData *createStampData() const;
@@ -123,14 +118,9 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
}
void initExecution() override;
void deinitExecution() override;
- CompositorPriority getRenderPriority() const override
+ eCompositorPriority getRenderPriority() const override
{
- return CompositorPriority::Low;
- }
-
- bool isFileOutputOperation() const override
- {
- return true;
+ return eCompositorPriority::Low;
}
};
@@ -146,3 +136,5 @@ void free_exr_channels(void *exrhandle,
const char *layerName,
const DataType datatype);
int get_datatype_size(DataType datatype);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PixelateOperation.cc b/source/blender/compositor/operations/COM_PixelateOperation.cc
index 0d810c80ab4..94827cd1b02 100644
--- a/source/blender/compositor/operations/COM_PixelateOperation.cc
+++ b/source/blender/compositor/operations/COM_PixelateOperation.cc
@@ -18,6 +18,8 @@
#include "COM_PixelateOperation.h"
+namespace blender::compositor {
+
PixelateOperation::PixelateOperation(DataType datatype)
{
this->addInputSocket(datatype);
@@ -45,3 +47,5 @@ void PixelateOperation::executePixelSampled(float output[4],
float ny = round(y);
this->m_inputOperation->readSampled(output, nx, ny, sampler);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PixelateOperation.h b/source/blender/compositor/operations/COM_PixelateOperation.h
index aae54965aa4..e8b272853da 100644
--- a/source/blender/compositor/operations/COM_PixelateOperation.h
+++ b/source/blender/compositor/operations/COM_PixelateOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* \brief Pixelate operation
*
@@ -60,3 +62,5 @@ class PixelateOperation : public NodeOperation {
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
index bcf79e1c6c6..3577860b93d 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
@@ -26,6 +26,8 @@
#include "BKE_node.h"
+namespace blender::compositor {
+
static bool check_corners(float corners[4][2])
{
int i, next, prev;
@@ -60,7 +62,7 @@ static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float c
{
for (int i = 0; i < 4; i++) {
float result[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- readers[i]->readSampled(result, rect->xmin, rect->ymin, COM_PS_NEAREST);
+ readers[i]->readSampled(result, rect->xmin, rect->ymin, PixelSampler::Nearest);
corners[i][0] = result[0];
corners[i][1] = result[1];
}
@@ -98,7 +100,7 @@ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(fal
* so we can use the initializeTileData function
* to read corners from input sockets ...
*/
- setComplex(true);
+ flags.complex = true;
}
void PlaneCornerPinMaskOperation::initExecution()
@@ -224,3 +226,5 @@ bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest(
input, readOperation, output);
#endif
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
index 6edecff0d54..91c0cd9e16b 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
@@ -27,6 +27,8 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+namespace blender::compositor {
+
class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation {
private:
bool m_corners_ready;
@@ -59,3 +61,5 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
index 87f837973d2..4edcc206f5b 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
@@ -29,6 +29,33 @@
#include "BKE_node.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
+PlaneDistortBaseOperation::PlaneDistortBaseOperation()
+ : m_motion_blur_samples(1), m_motion_blur_shutter(0.5f)
+{
+}
+
+void PlaneDistortBaseOperation::calculateCorners(const float corners[4][2],
+ bool normalized,
+ int sample)
+{
+ BLI_assert(sample < this->m_motion_blur_samples);
+ MotionSample *sample_data = &this->m_samples[sample];
+ if (normalized) {
+ for (int i = 0; i < 4; i++) {
+ sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
+ sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
+ }
+ }
+ else {
+ for (int i = 0; i < 4; i++) {
+ sample_data->frameSpaceCorners[i][0] = corners[i][0];
+ sample_data->frameSpaceCorners[i][1] = corners[i][1];
+ }
+ }
+}
+
/* ******** PlaneDistort WarpImage ******** */
BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], float deriv[2][2])
@@ -44,38 +71,25 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo
deriv[1][1] = (matrix[1][1] - matrix[1][2] * uv[1]) / vec[2];
}
-PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation()
+PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->m_pixelReader = nullptr;
- this->m_motion_blur_samples = 1;
- this->m_motion_blur_shutter = 0.5f;
- this->setComplex(true);
+ this->flags.complex = true;
}
void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2],
bool normalized,
int sample)
{
- BLI_assert(sample < this->m_motion_blur_samples);
+ PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample);
+
const int width = this->m_pixelReader->getWidth();
const int height = this->m_pixelReader->getHeight();
float frame_corners[4][2] = {
{0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}};
MotionSample *sample_data = &this->m_samples[sample];
- if (normalized) {
- for (int i = 0; i < 4; i++) {
- sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
- sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
- }
- }
- else {
- for (int i = 0; i < 4; i++) {
- sample_data->frameSpaceCorners[i][0] = corners[i][0];
- sample_data->frameSpaceCorners[i][1] = corners[i][1];
- }
- }
BKE_tracking_homography_between_two_quads(
sample_data->frameSpaceCorners, frame_corners, sample_data->perspectiveMatrix);
}
@@ -145,34 +159,12 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(
/* ******** PlaneDistort Mask ******** */
-PlaneDistortMaskOperation::PlaneDistortMaskOperation()
+PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation()
{
addOutputSocket(DataType::Value);
/* Currently hardcoded to 8 samples. */
m_osa = 8;
- this->m_motion_blur_samples = 1;
- this->m_motion_blur_shutter = 0.5f;
-}
-
-void PlaneDistortMaskOperation::calculateCorners(const float corners[4][2],
- bool normalized,
- int sample)
-{
- BLI_assert(sample < this->m_motion_blur_samples);
- MotionSample *sample_data = &this->m_samples[sample];
- if (normalized) {
- for (int i = 0; i < 4; i++) {
- sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
- sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
- }
- }
- else {
- for (int i = 0; i < 4; i++) {
- sample_data->frameSpaceCorners[i][0] = corners[i][0];
- sample_data->frameSpaceCorners[i][1] = corners[i][1];
- }
- }
}
void PlaneDistortMaskOperation::initExecution()
@@ -226,3 +218,5 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4],
output[0] = (float)inside_counter / (this->m_osa * this->m_motion_blur_samples);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
index c17f99a6710..cc6e4d00d71 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
@@ -28,23 +28,47 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+namespace blender::compositor {
+
#define PLANE_DISTORT_MAX_SAMPLES 64
-class PlaneDistortWarpImageOperation : public NodeOperation {
+class PlaneDistortBaseOperation : public NodeOperation {
protected:
struct MotionSample {
float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
float perspectiveMatrix[3][3];
};
- SocketReader *m_pixelReader;
MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES];
int m_motion_blur_samples;
float m_motion_blur_shutter;
public:
+ PlaneDistortBaseOperation();
+
+ void setMotionBlurSamples(int samples)
+ {
+ BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES);
+ this->m_motion_blur_samples = samples;
+ }
+ void setMotionBlurShutter(float shutter)
+ {
+ this->m_motion_blur_shutter = shutter;
+ }
+
+ virtual void calculateCorners(const float corners[4][2], bool normalized, int sample);
+
+ private:
+ friend class PlaneTrackCommon;
+};
+
+class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation {
+ protected:
+ SocketReader *m_pixelReader;
+
+ public:
PlaneDistortWarpImageOperation();
- void calculateCorners(const float corners[4][2], bool normalized, int sample);
+ void calculateCorners(const float corners[4][2], bool normalized, int sample) override;
void initExecution() override;
void deinitExecution() override;
@@ -54,45 +78,19 @@ class PlaneDistortWarpImageOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
-
- void setMotionBlurSamples(int samples)
- {
- BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES);
- this->m_motion_blur_samples = samples;
- }
- void setMotionBlurShutter(float shutter)
- {
- this->m_motion_blur_shutter = shutter;
- }
};
-class PlaneDistortMaskOperation : public NodeOperation {
+class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
protected:
- struct MotionSample {
- float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
- };
int m_osa;
- MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES];
float m_jitter[32][2];
- int m_motion_blur_samples;
- float m_motion_blur_shutter;
public:
PlaneDistortMaskOperation();
- void calculateCorners(const float corners[4][2], bool normalized, int sample);
-
void initExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
-
- void setMotionBlurSamples(int samples)
- {
- BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES);
- this->m_motion_blur_samples = samples;
- }
- void setMotionBlurShutter(float shutter)
- {
- this->m_motion_blur_shutter = shutter;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
index 81a598e937b..0884f2ad979 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
@@ -29,6 +29,8 @@
#include "BKE_node.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
/* ******** PlaneTrackCommon ******** */
PlaneTrackCommon::PlaneTrackCommon()
@@ -39,6 +41,26 @@ PlaneTrackCommon::PlaneTrackCommon()
this->m_planeTrackName[0] = '\0';
}
+void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *distort_op)
+{
+ float corners[4][2];
+ if (distort_op->m_motion_blur_samples == 1) {
+ readCornersFromTrack(corners, this->m_framenumber);
+ distort_op->calculateCorners(corners, true, 0);
+ }
+ else {
+ const float frame = (float)this->m_framenumber - distort_op->m_motion_blur_shutter;
+ const float frame_step = (distort_op->m_motion_blur_shutter * 2.0f) /
+ distort_op->m_motion_blur_samples;
+ float frame_iter = frame;
+ for (int sample = 0; sample < distort_op->m_motion_blur_samples; sample++) {
+ readCornersFromTrack(corners, frame_iter);
+ distort_op->calculateCorners(corners, true, sample);
+ frame_iter += frame_step;
+ }
+ }
+}
+
void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame)
{
MovieTracking *tracking;
@@ -82,21 +104,7 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2],
void PlaneTrackMaskOperation::initExecution()
{
PlaneDistortMaskOperation::initExecution();
- float corners[4][2];
- if (this->m_motion_blur_samples == 1) {
- readCornersFromTrack(corners, this->m_framenumber);
- calculateCorners(corners, true, 0);
- }
- else {
- const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter;
- const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples;
- float frame_iter = frame;
- for (int sample = 0; sample < this->m_motion_blur_samples; sample++) {
- readCornersFromTrack(corners, frame_iter);
- calculateCorners(corners, true, sample);
- frame_iter += frame_step;
- }
- }
+ PlaneTrackCommon::read_and_calculate_corners(this);
}
/* ******** PlaneTrackWarpImageOperation ******** */
@@ -104,20 +112,7 @@ void PlaneTrackMaskOperation::initExecution()
void PlaneTrackWarpImageOperation::initExecution()
{
PlaneDistortWarpImageOperation::initExecution();
- /* TODO(sergey): De-duplicate with mask operation. */
- float corners[4][2];
- if (this->m_motion_blur_samples == 1) {
- readCornersFromTrack(corners, this->m_framenumber);
- calculateCorners(corners, true, 0);
- }
- else {
- const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter;
- const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples;
- float frame_iter = frame;
- for (int sample = 0; sample < this->m_motion_blur_samples; sample++) {
- readCornersFromTrack(corners, frame_iter);
- calculateCorners(corners, true, sample);
- frame_iter += frame_step;
- }
- }
+ PlaneTrackCommon::read_and_calculate_corners(this);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
index 6cfb3accaa3..d240c8b06e9 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
@@ -28,6 +28,8 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+namespace blender::compositor {
+
class PlaneTrackCommon {
protected:
MovieClip *m_movieClip;
@@ -38,7 +40,7 @@ class PlaneTrackCommon {
/* note: this class is not an operation itself (to prevent virtual inheritance issues)
* implementation classes must make wrappers to use these methods, see below.
*/
- void readCornersFromTrack(float corners[4][2], float frame);
+ void read_and_calculate_corners(PlaneDistortBaseOperation *distort_op);
void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
public:
@@ -60,6 +62,9 @@ class PlaneTrackCommon {
{
this->m_framenumber = framenumber;
}
+
+ private:
+ void readCornersFromTrack(float corners[4][2], float frame);
};
class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTrackCommon {
@@ -97,3 +102,5 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation,
NodeOperation::determineResolution(temp, resolution);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc
index 6d1199ab118..e7c11613aa3 100644
--- a/source/blender/compositor/operations/COM_PreviewOperation.cc
+++ b/source/blender/compositor/operations/COM_PreviewOperation.cc
@@ -33,13 +33,15 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings,
const ColorManagedDisplaySettings *displaySettings,
const unsigned int defaultWidth,
const unsigned int defaultHeight)
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->m_preview = nullptr;
this->m_outputBuffer = nullptr;
this->m_input = nullptr;
@@ -48,6 +50,8 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings,
this->m_displaySettings = displaySettings;
this->m_defaultWidth = defaultWidth;
this->m_defaultHeight = defaultHeight;
+ flags.use_viewer_border = true;
+ flags.is_preview_operation = true;
}
void PreviewOperation::verifyPreview(bNodeInstanceHash *previews, bNodeInstanceKey key)
@@ -104,7 +108,7 @@ void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
color[1] = 0.0f;
color[2] = 0.0f;
color[3] = 1.0f;
- this->m_input->readSampled(color, rx, ry, COM_PS_NEAREST);
+ this->m_input->readSampled(color, rx, ry, PixelSampler::Nearest);
IMB_colormanagement_processor_apply_v4(cm_processor, color);
rgba_float_to_uchar(this->m_outputBuffer + offset, color);
offset += 4;
@@ -162,7 +166,9 @@ void PreviewOperation::determineResolution(unsigned int resolution[2],
resolution[1] = height;
}
-CompositorPriority PreviewOperation::getRenderPriority() const
+eCompositorPriority PreviewOperation::getRenderPriority() const
{
- return CompositorPriority::Low;
+ return eCompositorPriority::Low;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h
index 36ae5f59bb9..0f43f01c5d6 100644
--- a/source/blender/compositor/operations/COM_PreviewOperation.h
+++ b/source/blender/compositor/operations/COM_PreviewOperation.h
@@ -24,6 +24,8 @@
#include "DNA_color_types.h"
#include "DNA_image_types.h"
+namespace blender::compositor {
+
class PreviewOperation : public NodeOperation {
protected:
unsigned char *m_outputBuffer;
@@ -53,7 +55,7 @@ class PreviewOperation : public NodeOperation {
}
void initExecution() override;
void deinitExecution() override;
- CompositorPriority getRenderPriority() const override;
+ eCompositorPriority getRenderPriority() const override;
void executeRegion(rcti *rect, unsigned int tileNumber) override;
void determineResolution(unsigned int resolution[2],
@@ -61,8 +63,6 @@ class PreviewOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
- bool isPreviewOperation() const override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
index 5494c3cd46b..93702d3f0cf 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
@@ -20,12 +20,14 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
ProjectorLensDistortionOperation::ProjectorLensDistortionOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
this->m_dispersionAvailable = false;
this->m_dispersion = 0.0f;
@@ -103,7 +105,7 @@ void ProjectorLensDistortionOperation::updateDispersion()
this->lockMutex();
if (!this->m_dispersionAvailable) {
float result[4];
- this->getInputSocketReader(1)->readSampled(result, 1, 1, COM_PS_NEAREST);
+ this->getInputSocketReader(1)->readSampled(result, 1, 1, PixelSampler::Nearest);
this->m_dispersion = result[0];
this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f);
this->m_kr2 = this->m_kr * 20;
@@ -111,3 +113,5 @@ void ProjectorLensDistortionOperation::updateDispersion()
}
this->unlockMutex();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
index b47d4f37589..bce61d3de15 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class ProjectorLensDistortionOperation : public NodeOperation {
private:
/**
@@ -58,3 +60,5 @@ class ProjectorLensDistortionOperation : public NodeOperation {
void updateDispersion();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.cc b/source/blender/compositor/operations/COM_QualityStepHelper.cc
index c0d86314fb7..e347d278ce4 100644
--- a/source/blender/compositor/operations/COM_QualityStepHelper.cc
+++ b/source/blender/compositor/operations/COM_QualityStepHelper.cc
@@ -18,9 +18,11 @@
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
QualityStepHelper::QualityStepHelper()
{
- this->m_quality = CompositorQuality::High;
+ this->m_quality = eCompositorQuality::High;
this->m_step = 1;
this->m_offsetadd = 4;
}
@@ -30,16 +32,16 @@ void QualityStepHelper::initExecution(QualityHelper helper)
switch (helper) {
case COM_QH_INCREASE:
switch (this->m_quality) {
- case CompositorQuality::High:
+ case eCompositorQuality::High:
default:
this->m_step = 1;
this->m_offsetadd = 1;
break;
- case CompositorQuality::Medium:
+ case eCompositorQuality::Medium:
this->m_step = 2;
this->m_offsetadd = 2;
break;
- case CompositorQuality::Low:
+ case eCompositorQuality::Low:
this->m_step = 3;
this->m_offsetadd = 3;
break;
@@ -47,16 +49,16 @@ void QualityStepHelper::initExecution(QualityHelper helper)
break;
case COM_QH_MULTIPLY:
switch (this->m_quality) {
- case CompositorQuality::High:
+ case eCompositorQuality::High:
default:
this->m_step = 1;
this->m_offsetadd = 4;
break;
- case CompositorQuality::Medium:
+ case eCompositorQuality::Medium:
this->m_step = 2;
this->m_offsetadd = 8;
break;
- case CompositorQuality::Low:
+ case eCompositorQuality::Low:
this->m_step = 4;
this->m_offsetadd = 16;
break;
@@ -64,3 +66,5 @@ void QualityStepHelper::initExecution(QualityHelper helper)
break;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.h b/source/blender/compositor/operations/COM_QualityStepHelper.h
index e437613fb29..c5f16a58686 100644
--- a/source/blender/compositor/operations/COM_QualityStepHelper.h
+++ b/source/blender/compositor/operations/COM_QualityStepHelper.h
@@ -18,7 +18,9 @@
#pragma once
-#include "COM_defines.h"
+#include "COM_Enums.h"
+
+namespace blender::compositor {
typedef enum QualityHelper {
COM_QH_INCREASE,
@@ -27,7 +29,7 @@ typedef enum QualityHelper {
class QualityStepHelper {
private:
- CompositorQuality m_quality;
+ eCompositorQuality m_quality;
int m_step;
int m_offsetadd;
@@ -49,8 +51,10 @@ class QualityStepHelper {
public:
QualityStepHelper();
- void setQuality(CompositorQuality quality)
+ void setQuality(eCompositorQuality quality)
{
this->m_quality = quality;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc
index 2977e6685d2..cc58f29e8d9 100644
--- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc
+++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc
@@ -20,12 +20,15 @@
#include "COM_WriteBufferOperation.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
ReadBufferOperation::ReadBufferOperation(DataType datatype)
{
this->addOutputSocket(datatype);
this->m_single_value = false;
this->m_offset = 0;
this->m_buffer = nullptr;
+ flags.is_read_buffer_operation = true;
}
void *ReadBufferOperation::initializeTileData(rcti * /*rect*/)
@@ -60,14 +63,14 @@ void ReadBufferOperation::executePixelSampled(float output[4],
}
else {
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
m_buffer->read(output, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
default:
m_buffer->readBilinear(output, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
m_buffer->readBilinear(output, x, y);
break;
}
@@ -85,7 +88,7 @@ void ReadBufferOperation::executePixelExtend(float output[4],
/* write buffer has a single value stored at (0,0) */
m_buffer->read(output, 0, 0);
}
- else if (sampler == COM_PS_NEAREST) {
+ else if (sampler == PixelSampler::Nearest) {
m_buffer->read(output, x, y, extend_x, extend_y);
}
else {
@@ -131,3 +134,5 @@ void ReadBufferOperation::updateMemoryBuffer()
{
this->m_buffer = this->getMemoryProxy()->getBuffer();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h
index 21ef716b727..8b96b961a43 100644
--- a/source/blender/compositor/operations/COM_ReadBufferOperation.h
+++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h
@@ -22,6 +22,8 @@
#include "COM_MemoryProxy.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ReadBufferOperation : public NodeOperation {
private:
MemoryProxy *m_memoryProxy;
@@ -35,10 +37,12 @@ class ReadBufferOperation : public NodeOperation {
{
this->m_memoryProxy = memoryProxy;
}
- MemoryProxy *getMemoryProxy()
+
+ MemoryProxy *getMemoryProxy() const
{
return this->m_memoryProxy;
}
+
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
@@ -51,10 +55,6 @@ class ReadBufferOperation : public NodeOperation {
MemoryBufferExtend extend_x,
MemoryBufferExtend extend_y);
void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override;
- bool isReadBufferOperation() const override
- {
- return true;
- }
void setOffset(unsigned int offset)
{
this->m_offset = offset;
@@ -73,3 +73,5 @@ class ReadBufferOperation : public NodeOperation {
void readResolutionFromWriteBuffer();
void updateMemoryBuffer();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc
index d622e14b585..1ac451b95c2 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.cc
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc
@@ -32,6 +32,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+namespace blender::compositor {
+
/* ******** Render Layers Base Prog ******** */
RenderLayersProg::RenderLayersProg(const char *passName, DataType type, int elementsize)
@@ -92,7 +94,7 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS
}
switch (sampler) {
- case COM_PS_NEAREST: {
+ case PixelSampler::Nearest: {
offset = (iy * width + ix) * this->m_elementsize;
if (this->m_elementsize == 1) {
@@ -107,12 +109,12 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS
break;
}
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
BLI_bilinear_interpolation_fl(
this->m_inputBuffer, output, width, height, this->m_elementsize, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
BLI_bicubic_interpolation_fl(
this->m_inputBuffer, output, width, height, this->m_elementsize, x, y);
break;
@@ -216,7 +218,7 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2],
}
}
-std::unique_ptr<MetaData> RenderLayersProg::getMetaData() const
+std::unique_ptr<MetaData> RenderLayersProg::getMetaData()
{
Scene *scene = this->getScene();
Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr;
@@ -306,3 +308,5 @@ void RenderLayersDepthProg::executePixelSampled(float output[4],
output[0] = inputBuffer[offset];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h
index f3e1c892a83..33e4fb163c5 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.h
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.h
@@ -26,6 +26,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
/**
* Base class for all renderlayeroperations
*
@@ -123,7 +125,7 @@ class RenderLayersProg : public NodeOperation {
void deinitExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- std::unique_ptr<MetaData> getMetaData() const override;
+ std::unique_ptr<MetaData> getMetaData() override;
};
class RenderLayersAOOperation : public RenderLayersProg {
@@ -152,3 +154,5 @@ class RenderLayersDepthProg : public RenderLayersProg {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc
index c2105efe246..4fb3d324992 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.cc
+++ b/source/blender/compositor/operations/COM_RotateOperation.cc
@@ -19,6 +19,8 @@
#include "COM_RotateOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
RotateOperation::RotateOperation()
{
this->addInputSocket(DataType::Color);
@@ -48,7 +50,7 @@ inline void RotateOperation::ensureDegree()
{
if (!this->m_isDegreeSet) {
float degree[4];
- this->m_degreeSocket->readSampled(degree, 0, 0, COM_PS_NEAREST);
+ this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest);
double rad;
if (this->m_doDegree2RadConversion) {
rad = DEG2RAD((double)degree[0]);
@@ -105,3 +107,5 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h
index 9b9d5987a8e..d76507f9816 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.h
+++ b/source/blender/compositor/operations/COM_RotateOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class RotateOperation : public NodeOperation {
private:
SocketReader *m_imageSocket;
@@ -46,3 +48,5 @@ class RotateOperation : public NodeOperation {
void ensureDegree();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc
new file mode 100644
index 00000000000..74807f281d7
--- /dev/null
+++ b/source/blender/compositor/operations/COM_SMAAOperation.cc
@@ -0,0 +1,868 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ *
+ * Contributor: IRIE Shinsuke
+ */
+
+#include "COM_SMAAOperation.h"
+#include "BLI_math.h"
+#include "COM_SMAAAreaTexture.h"
+
+extern "C" {
+#include "IMB_colormanagement.h"
+}
+
+namespace blender::compositor {
+
+/*
+ * An implementation of Enhanced Subpixel Morphological Antialiasing (SMAA)
+ *
+ * The algorithm was proposed by:
+ * Jorge Jimenez, Jose I. Echevarria, Tiago Sousa, Diego Gutierrez
+ *
+ * http://www.iryoku.com/smaa/
+ *
+ * This file is based on smaa-cpp:
+ *
+ * https://github.com/iRi-E/smaa-cpp
+ *
+ * Currently only SMAA 1x mode is provided, so the operation will be done
+ * with no spatial multisampling nor temporal supersampling.
+ *
+ * Note: This program assumes the screen coordinates are DirectX style, so
+ * the vertical direction is upside-down. "top" and "bottom" actually mean
+ * bottom and top, respectively.
+ */
+
+/*-----------------------------------------------------------------------------*/
+/* Non-Configurable Defines */
+
+#define SMAA_AREATEX_SIZE 80
+#define SMAA_AREATEX_MAX_DISTANCE 20
+#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20
+#define SMAA_MAX_SEARCH_STEPS 362 /* 362 - 1 = 19^2 */
+#define SMAA_MAX_SEARCH_STEPS_DIAG 19
+
+/*-----------------------------------------------------------------------------*/
+/* Internal Functions to Sample Pixel Color from Image */
+
+static inline void sample(SocketReader *reader, int x, int y, float color[4])
+{
+ if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) {
+ color[0] = color[1] = color[2] = color[3] = 0.0;
+ return;
+ }
+
+ reader->read(color, x, y, nullptr);
+}
+
+static void sample_bilinear_vertical(
+ SocketReader *reader, int x, int y, float yoffset, float color[4])
+{
+ float iy = floorf(yoffset);
+ float fy = yoffset - iy;
+ y += (int)iy;
+
+ float color00[4], color01[4];
+
+ sample(reader, x + 0, y + 0, color00);
+ sample(reader, x + 0, y + 1, color01);
+
+ color[0] = interpf(color01[0], color00[0], fy);
+ color[1] = interpf(color01[1], color00[1], fy);
+ color[2] = interpf(color01[2], color00[2], fy);
+ color[3] = interpf(color01[3], color00[3], fy);
+}
+
+static void sample_bilinear_horizontal(
+ SocketReader *reader, int x, int y, float xoffset, float color[4])
+{
+ float ix = floorf(xoffset);
+ float fx = xoffset - ix;
+ x += (int)ix;
+
+ float color00[4], color10[4];
+
+ sample(reader, x + 0, y + 0, color00);
+ sample(reader, x + 1, y + 0, color10);
+
+ color[0] = interpf(color10[0], color00[0], fx);
+ color[1] = interpf(color10[1], color00[1], fx);
+ color[2] = interpf(color10[2], color00[2], fx);
+ color[3] = interpf(color10[3], color00[3], fx);
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Internal Functions to Sample Blending Weights from AreaTex */
+
+static inline const float *areatex_sample_internal(const float *areatex, int x, int y)
+{
+ return &areatex[(CLAMPIS(x, 0, SMAA_AREATEX_SIZE - 1) +
+ CLAMPIS(y, 0, SMAA_AREATEX_SIZE - 1) * SMAA_AREATEX_SIZE) *
+ 2];
+}
+
+/**
+ * We have the distance and both crossing edges. So, what are the areas
+ * at each side of current edge?
+ */
+static void area(int d1, int d2, int e1, int e2, float weights[2])
+{
+ /* The areas texture is compressed quadratically: */
+ float x = (float)(SMAA_AREATEX_MAX_DISTANCE * e1) + sqrtf((float)d1);
+ float y = (float)(SMAA_AREATEX_MAX_DISTANCE * e2) + sqrtf((float)d2);
+
+ float ix = floorf(x), iy = floorf(y);
+ float fx = x - ix, fy = y - iy;
+ int X = (int)ix, Y = (int)iy;
+
+ const float *weights00 = areatex_sample_internal(areatex, X + 0, Y + 0);
+ const float *weights10 = areatex_sample_internal(areatex, X + 1, Y + 0);
+ const float *weights01 = areatex_sample_internal(areatex, X + 0, Y + 1);
+ const float *weights11 = areatex_sample_internal(areatex, X + 1, Y + 1);
+
+ weights[0] = interpf(
+ interpf(weights11[0], weights01[0], fx), interpf(weights10[0], weights00[0], fx), fy);
+ weights[1] = interpf(
+ interpf(weights11[1], weights01[1], fx), interpf(weights10[1], weights00[1], fx), fy);
+}
+
+/**
+ * Similar to area(), this calculates the area corresponding to a certain
+ * diagonal distance and crossing edges 'e'.
+ */
+static void area_diag(int d1, int d2, int e1, int e2, float weights[2])
+{
+ int x = SMAA_AREATEX_MAX_DISTANCE_DIAG * e1 + d1;
+ int y = SMAA_AREATEX_MAX_DISTANCE_DIAG * e2 + d2;
+
+ const float *w = areatex_sample_internal(areatex_diag, x, y);
+ copy_v2_v2(weights, w);
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Edge Detection (First Pass) */
+/*-----------------------------------------------------------------------------*/
+
+SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation()
+{
+ this->addInputSocket(DataType::Color); /* image */
+ this->addInputSocket(DataType::Value); /* depth, material ID, etc. */
+ this->addOutputSocket(DataType::Color);
+ this->flags.complex = true;
+ this->m_imageReader = nullptr;
+ this->m_valueReader = nullptr;
+ this->m_threshold = 0.1f;
+ this->m_contrast_limit = 2.0f;
+}
+
+void SMAAEdgeDetectionOperation::initExecution()
+{
+ this->m_imageReader = this->getInputSocketReader(0);
+ this->m_valueReader = this->getInputSocketReader(1);
+}
+
+void SMAAEdgeDetectionOperation::deinitExecution()
+{
+ this->m_imageReader = nullptr;
+ this->m_valueReader = nullptr;
+}
+
+void SMAAEdgeDetectionOperation::setThreshold(float threshold)
+{
+ /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 0.5 */
+ m_threshold = scalenorm(0, 0.5, threshold);
+}
+
+void SMAAEdgeDetectionOperation::setLocalContrastAdaptationFactor(float factor)
+{
+ /* UI values are between 0 and 1 for simplicity but algorithm expects values between 1 and 10 */
+ m_contrast_limit = scalenorm(1, 10, factor);
+}
+
+bool SMAAEdgeDetectionOperation::determineDependingAreaOfInterest(
+ rcti *input, ReadBufferOperation *readOperation, rcti *output)
+{
+ rcti newInput;
+ newInput.xmax = input->xmax + 1;
+ newInput.xmin = input->xmin - 2;
+ newInput.ymax = input->ymax + 1;
+ newInput.ymin = input->ymin - 2;
+
+ return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
+}
+
+void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/)
+{
+ float color[4];
+
+ /* Calculate luma deltas: */
+ sample(m_imageReader, x, y, color);
+ float L = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x - 1, y, color);
+ float Lleft = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x, y - 1, color);
+ float Ltop = IMB_colormanagement_get_luminance(color);
+ float Dleft = fabsf(L - Lleft);
+ float Dtop = fabsf(L - Ltop);
+
+ /* We do the usual threshold: */
+ output[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f;
+ output[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f;
+ output[2] = 0.0f;
+ output[3] = 1.0f;
+
+ /* Then discard if there is no edge: */
+ if (is_zero_v2(output)) {
+ return;
+ }
+
+ /* Calculate right and bottom deltas: */
+ sample(m_imageReader, x + 1, y, color);
+ float Lright = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x, y + 1, color);
+ float Lbottom = IMB_colormanagement_get_luminance(color);
+ float Dright = fabsf(L - Lright);
+ float Dbottom = fabsf(L - Lbottom);
+
+ /* Calculate the maximum delta in the direct neighborhood: */
+ float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom));
+
+ /* Calculate luma used for both left and top edges: */
+ sample(m_imageReader, x - 1, y - 1, color);
+ float Llefttop = IMB_colormanagement_get_luminance(color);
+
+ /* Left edge */
+ if (output[0] != 0.0f) {
+ /* Calculate deltas around the left pixel: */
+ sample(m_imageReader, x - 2, y, color);
+ float Lleftleft = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x - 1, y + 1, color);
+ float Lleftbottom = IMB_colormanagement_get_luminance(color);
+ float Dleftleft = fabsf(Lleft - Lleftleft);
+ float Dlefttop = fabsf(Lleft - Llefttop);
+ float Dleftbottom = fabsf(Lleft - Lleftbottom);
+
+ /* Calculate the final maximum delta: */
+ maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom)));
+
+ /* Local contrast adaptation: */
+ if (maxDelta > m_contrast_limit * Dleft) {
+ output[0] = 0.0f;
+ }
+ }
+
+ /* Top edge */
+ if (output[1] != 0.0f) {
+ /* Calculate top-top delta: */
+ sample(m_imageReader, x, y - 2, color);
+ float Ltoptop = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x + 1, y - 1, color);
+ float Ltopright = IMB_colormanagement_get_luminance(color);
+ float Dtoptop = fabsf(Ltop - Ltoptop);
+ float Dtopleft = fabsf(Ltop - Llefttop);
+ float Dtopright = fabsf(Ltop - Ltopright);
+
+ /* Calculate the final maximum delta: */
+ maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright)));
+
+ /* Local contrast adaptation: */
+ if (maxDelta > m_contrast_limit * Dtop) {
+ output[1] = 0.0f;
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Blending Weight Calculation (Second Pass) */
+/*-----------------------------------------------------------------------------*/
+
+SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation()
+{
+ this->addInputSocket(DataType::Color); /* edges */
+ this->addOutputSocket(DataType::Color);
+ this->flags.complex = true;
+ this->m_imageReader = nullptr;
+ this->m_corner_rounding = 25;
+}
+
+void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect)
+{
+ return getInputOperation(0)->initializeTileData(rect);
+}
+
+void SMAABlendingWeightCalculationOperation::initExecution()
+{
+ this->m_imageReader = this->getInputSocketReader(0);
+}
+
+void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding)
+{
+ /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 100 */
+ m_corner_rounding = static_cast<int>(scalenorm(0, 100, rounding));
+}
+
+void SMAABlendingWeightCalculationOperation::executePixel(float output[4],
+ int x,
+ int y,
+ void * /*data*/)
+{
+ float edges[4], c[4];
+
+ zero_v4(output);
+ sample(m_imageReader, x, y, edges);
+
+ /* Edge at north */
+ if (edges[1] > 0.0f) {
+ /* Diagonals have both north and west edges, so calculating weights for them */
+ /* in one of the boundaries is enough. */
+ calculateDiagWeights(x, y, edges, output);
+
+ /* We give priority to diagonals, so if we find a diagonal we skip */
+ /* horizontal/vertical processing. */
+ if (!is_zero_v2(output)) {
+ return;
+ }
+
+ /* Find the distance to the left and the right: */
+ int left = searchXLeft(x, y);
+ int right = searchXRight(x, y);
+ int d1 = x - left, d2 = right - x;
+
+ /* Fetch the left and right crossing edges: */
+ int e1 = 0, e2 = 0;
+ sample(m_imageReader, left, y - 1, c);
+ if (c[0] > 0.0) {
+ e1 += 1;
+ }
+ sample(m_imageReader, left, y, c);
+ if (c[0] > 0.0) {
+ e1 += 2;
+ }
+ sample(m_imageReader, right + 1, y - 1, c);
+ if (c[0] > 0.0) {
+ e2 += 1;
+ }
+ sample(m_imageReader, right + 1, y, c);
+ if (c[0] > 0.0) {
+ e2 += 2;
+ }
+
+ /* Ok, we know how this pattern looks like, now it is time for getting */
+ /* the actual area: */
+ area(d1, d2, e1, e2, output); /* R, G */
+
+ /* Fix corners: */
+ if (m_corner_rounding) {
+ detectHorizontalCornerPattern(output, left, right, y, d1, d2);
+ }
+ }
+
+ /* Edge at west */
+ if (edges[0] > 0.0f) {
+ /* Did we already do diagonal search for this west edge from the left neighboring pixel? */
+ if (isVerticalSearchUnneeded(x, y)) {
+ return;
+ }
+
+ /* Find the distance to the top and the bottom: */
+ int top = searchYUp(x, y);
+ int bottom = searchYDown(x, y);
+ int d1 = y - top, d2 = bottom - y;
+
+ /* Fetch the top and bottom crossing edges: */
+ int e1 = 0, e2 = 0;
+ sample(m_imageReader, x - 1, top, c);
+ if (c[1] > 0.0) {
+ e1 += 1;
+ }
+ sample(m_imageReader, x, top, c);
+ if (c[1] > 0.0) {
+ e1 += 2;
+ }
+ sample(m_imageReader, x - 1, bottom + 1, c);
+ if (c[1] > 0.0) {
+ e2 += 1;
+ }
+ sample(m_imageReader, x, bottom + 1, c);
+ if (c[1] > 0.0) {
+ e2 += 2;
+ }
+
+ /* Get the area for this direction: */
+ area(d1, d2, e1, e2, output + 2); /* B, A */
+
+ /* Fix corners: */
+ if (m_corner_rounding) {
+ detectVerticalCornerPattern(output + 2, x, top, bottom, d1, d2);
+ }
+ }
+}
+
+void SMAABlendingWeightCalculationOperation::deinitExecution()
+{
+ this->m_imageReader = nullptr;
+}
+
+bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest(
+ rcti *input, ReadBufferOperation *readOperation, rcti *output)
+{
+ rcti newInput;
+
+ newInput.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1);
+ newInput.xmin = input->xmin -
+ fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1);
+ newInput.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG);
+ newInput.ymin = input->ymin -
+ fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG);
+
+ return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Diagonal Search Functions */
+
+/**
+ * These functions allows to perform diagonal pattern searches.
+ */
+int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, bool *found)
+{
+ float e[4];
+ int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir;
+ *found = false;
+
+ while (x != end) {
+ x += dir;
+ y -= dir;
+ sample(m_imageReader, x, y, e);
+ if (e[1] == 0.0f) {
+ *found = true;
+ break;
+ }
+ if (e[0] == 0.0f) {
+ *found = true;
+ return (dir < 0) ? x : x - dir;
+ }
+ }
+
+ return x - dir;
+}
+
+int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, bool *found)
+{
+ float e[4];
+ int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir;
+ *found = false;
+
+ while (x != end) {
+ x += dir;
+ y += dir;
+ sample(m_imageReader, x, y, e);
+ if (e[1] == 0.0f) {
+ *found = true;
+ break;
+ }
+ sample(m_imageReader, x + 1, y, e);
+ if (e[0] == 0.0f) {
+ *found = true;
+ return (dir > 0) ? x : x - dir;
+ }
+ }
+
+ return x - dir;
+}
+
+/**
+ * This searches for diagonal patterns and returns the corresponding weights.
+ */
+void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x,
+ int y,
+ const float edges[2],
+ float weights[2])
+{
+ int d1, d2;
+ bool d1_found, d2_found;
+ float e[4], c[4];
+
+ zero_v2(weights);
+
+ if (SMAA_MAX_SEARCH_STEPS_DIAG <= 0) {
+ return;
+ }
+
+ /* Search for the line ends: */
+ if (edges[0] > 0.0f) {
+ d1 = x - searchDiag1(x, y, -1, &d1_found);
+ }
+ else {
+ d1 = 0;
+ d1_found = true;
+ }
+ d2 = searchDiag1(x, y, 1, &d2_found) - x;
+
+ if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */
+ int e1 = 0, e2 = 0;
+
+ if (d1_found) {
+ /* Fetch the crossing edges: */
+ int left = x - d1, bottom = y + d1;
+
+ sample(m_imageReader, left - 1, bottom, c);
+ if (c[1] > 0.0) {
+ e1 += 2;
+ }
+ sample(m_imageReader, left, bottom, c);
+ if (c[0] > 0.0) {
+ e1 += 1;
+ }
+ }
+
+ if (d2_found) {
+ /* Fetch the crossing edges: */
+ int right = x + d2, top = y - d2;
+
+ sample(m_imageReader, right + 1, top, c);
+ if (c[1] > 0.0) {
+ e2 += 2;
+ }
+ sample(m_imageReader, right + 1, top - 1, c);
+ if (c[0] > 0.0) {
+ e2 += 1;
+ }
+ }
+
+ /* Fetch the areas for this line: */
+ area_diag(d1, d2, e1, e2, weights);
+ }
+
+ /* Search for the line ends: */
+ d1 = x - searchDiag2(x, y, -1, &d1_found);
+ sample(m_imageReader, x + 1, y, e);
+ if (e[0] > 0.0f) {
+ d2 = searchDiag2(x, y, 1, &d2_found) - x;
+ }
+ else {
+ d2 = 0;
+ d2_found = true;
+ }
+
+ if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */
+ int e1 = 0, e2 = 0;
+
+ if (d1_found) {
+ /* Fetch the crossing edges: */
+ int left = x - d1, top = y - d1;
+
+ sample(m_imageReader, left - 1, top, c);
+ if (c[1] > 0.0) {
+ e1 += 2;
+ }
+ sample(m_imageReader, left, top - 1, c);
+ if (c[0] > 0.0) {
+ e1 += 1;
+ }
+ }
+
+ if (d2_found) {
+ /* Fetch the crossing edges: */
+ int right = x + d2, bottom = y + d2;
+
+ sample(m_imageReader, right + 1, bottom, c);
+ if (c[1] > 0.0) {
+ e2 += 2;
+ }
+ if (c[0] > 0.0) {
+ e2 += 1;
+ }
+ }
+
+ /* Fetch the areas for this line: */
+ float w[2];
+ area_diag(d1, d2, e1, e2, w);
+ weights[0] += w[1];
+ weights[1] += w[0];
+ }
+}
+
+bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int y)
+{
+ int d1, d2;
+ bool found;
+ float e[4];
+
+ if (SMAA_MAX_SEARCH_STEPS_DIAG <= 0) {
+ return false;
+ }
+
+ /* Search for the line ends: */
+ sample(m_imageReader, x - 1, y, e);
+ if (e[1] > 0.0f) {
+ d1 = x - searchDiag2(x - 1, y, -1, &found);
+ }
+ else {
+ d1 = 0;
+ }
+ d2 = searchDiag2(x - 1, y, 1, &found) - x;
+
+ return (d1 + d2 > 2); /* d1 + d2 + 1 > 3 */
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Horizontal/Vertical Search Functions */
+
+int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y)
+{
+ int end = x - SMAA_MAX_SEARCH_STEPS;
+ float e[4];
+
+ while (x > end) {
+ sample(m_imageReader, x, y, e);
+ if (e[1] == 0.0f) { /* Is the edge not activated? */
+ break;
+ }
+ if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ return x;
+ }
+ sample(m_imageReader, x, y - 1, e);
+ if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ return x;
+ }
+ x--;
+ }
+
+ return x + 1;
+}
+
+int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y)
+{
+ int end = x + SMAA_MAX_SEARCH_STEPS;
+ float e[4];
+
+ while (x < end) {
+ x++;
+ sample(m_imageReader, x, y, e);
+ if (e[1] == 0.0f || /* Is the edge not activated? */
+ e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ break;
+ }
+ sample(m_imageReader, x, y - 1, e);
+ if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ break;
+ }
+ }
+
+ return x - 1;
+}
+
+int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y)
+{
+ int end = y - SMAA_MAX_SEARCH_STEPS;
+ float e[4];
+
+ while (y > end) {
+ sample(m_imageReader, x, y, e);
+ if (e[0] == 0.0f) { /* Is the edge not activated? */
+ break;
+ }
+ if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ return y;
+ }
+ sample(m_imageReader, x - 1, y, e);
+ if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ return y;
+ }
+ y--;
+ }
+
+ return y + 1;
+}
+
+int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y)
+{
+ int end = y + SMAA_MAX_SEARCH_STEPS;
+ float e[4];
+
+ while (y < end) {
+ y++;
+ sample(m_imageReader, x, y, e);
+ if (e[0] == 0.0f || /* Is the edge not activated? */
+ e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ break;
+ }
+ sample(m_imageReader, x - 1, y, e);
+ if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ break;
+ }
+ }
+
+ return y - 1;
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Corner Detection Functions */
+
+void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern(
+ float weights[2], int left, int right, int y, int d1, int d2)
+{
+ float factor[2] = {1.0f, 1.0f};
+ float rounding = m_corner_rounding / 100.0f;
+ float e[4];
+
+ /* Reduce blending for pixels in the center of a line. */
+ rounding *= (d1 == d2) ? 0.5f : 1.0f;
+
+ /* Near the left corner */
+ if (d1 <= d2) {
+ sample(m_imageReader, left, y + 1, e);
+ factor[0] -= rounding * e[0];
+ sample(m_imageReader, left, y - 2, e);
+ factor[1] -= rounding * e[0];
+ }
+ /* Near the right corner */
+ if (d1 >= d2) {
+ sample(m_imageReader, right + 1, y + 1, e);
+ factor[0] -= rounding * e[0];
+ sample(m_imageReader, right + 1, y - 2, e);
+ factor[1] -= rounding * e[0];
+ }
+
+ weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f);
+ weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f);
+}
+
+void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern(
+ float weights[2], int x, int top, int bottom, int d1, int d2)
+{
+ float factor[2] = {1.0f, 1.0f};
+ float rounding = m_corner_rounding / 100.0f;
+ float e[4];
+
+ /* Reduce blending for pixels in the center of a line. */
+ rounding *= (d1 == d2) ? 0.5f : 1.0f;
+
+ /* Near the top corner */
+ if (d1 <= d2) {
+ sample(m_imageReader, x + 1, top, e);
+ factor[0] -= rounding * e[1];
+ sample(m_imageReader, x - 2, top, e);
+ factor[1] -= rounding * e[1];
+ }
+ /* Near the bottom corner */
+ if (d1 >= d2) {
+ sample(m_imageReader, x + 1, bottom + 1, e);
+ factor[0] -= rounding * e[1];
+ sample(m_imageReader, x - 2, bottom + 1, e);
+ factor[1] -= rounding * e[1];
+ }
+
+ weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f);
+ weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f);
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Neighborhood Blending (Third Pass) */
+/*-----------------------------------------------------------------------------*/
+
+SMAANeighborhoodBlendingOperation::SMAANeighborhoodBlendingOperation()
+{
+ this->addInputSocket(DataType::Color); /* image */
+ this->addInputSocket(DataType::Color); /* blend */
+ this->addOutputSocket(DataType::Color);
+ this->flags.complex = true;
+ this->m_image1Reader = nullptr;
+ this->m_image2Reader = nullptr;
+}
+
+void *SMAANeighborhoodBlendingOperation::initializeTileData(rcti *rect)
+{
+ return getInputOperation(0)->initializeTileData(rect);
+}
+
+void SMAANeighborhoodBlendingOperation::initExecution()
+{
+ this->m_image1Reader = this->getInputSocketReader(0);
+ this->m_image2Reader = this->getInputSocketReader(1);
+}
+
+void SMAANeighborhoodBlendingOperation::executePixel(float output[4],
+ int x,
+ int y,
+ void * /*data*/)
+{
+ float w[4];
+
+ /* Fetch the blending weights for current pixel: */
+ sample(m_image2Reader, x, y, w);
+ float left = w[2], top = w[0];
+ sample(m_image2Reader, x + 1, y, w);
+ float right = w[3];
+ sample(m_image2Reader, x, y + 1, w);
+ float bottom = w[1];
+
+ /* Is there any blending weight with a value greater than 0.0? */
+ if (right + bottom + left + top < 1e-5f) {
+ sample(m_image1Reader, x, y, output);
+ return;
+ }
+
+ /* Calculate the blending offsets: */
+ void (*samplefunc)(SocketReader * reader, int x, int y, float xoffset, float color[4]);
+ float offset1, offset2, weight1, weight2, color1[4], color2[4];
+
+ if (fmaxf(right, left) > fmaxf(bottom, top)) { /* max(horizontal) > max(vertical) */
+ samplefunc = sample_bilinear_horizontal;
+ offset1 = right;
+ offset2 = -left;
+ weight1 = right / (right + left);
+ weight2 = left / (right + left);
+ }
+ else {
+ samplefunc = sample_bilinear_vertical;
+ offset1 = bottom;
+ offset2 = -top;
+ weight1 = bottom / (bottom + top);
+ weight2 = top / (bottom + top);
+ }
+
+ /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */
+ samplefunc(m_image1Reader, x, y, offset1, color1);
+ samplefunc(m_image1Reader, x, y, offset2, color2);
+
+ mul_v4_v4fl(output, color1, weight1);
+ madd_v4_v4fl(output, color2, weight2);
+}
+
+void SMAANeighborhoodBlendingOperation::deinitExecution()
+{
+ this->m_image1Reader = nullptr;
+ this->m_image2Reader = nullptr;
+}
+
+bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest(
+ rcti *input, ReadBufferOperation *readOperation, rcti *output)
+{
+ rcti newInput;
+
+ newInput.xmax = input->xmax + 1;
+ newInput.xmin = input->xmin - 1;
+ newInput.ymax = input->ymax + 1;
+ newInput.ymin = input->ymin - 1;
+
+ return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.h b/source/blender/compositor/operations/COM_SMAAOperation.h
new file mode 100644
index 00000000000..781762202b4
--- /dev/null
+++ b/source/blender/compositor/operations/COM_SMAAOperation.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ *
+ * Contributor: IRIE Shinsuke
+ */
+
+#pragma once
+
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+/*-----------------------------------------------------------------------------*/
+/* Edge Detection (First Pass) */
+
+class SMAAEdgeDetectionOperation : public NodeOperation {
+ protected:
+ SocketReader *m_imageReader;
+ SocketReader *m_valueReader;
+
+ float m_threshold;
+ float m_contrast_limit;
+
+ public:
+ SMAAEdgeDetectionOperation();
+
+ /**
+ * the inner loop of this program
+ */
+ virtual void executePixel(float output[4], int x, int y, void *data) override;
+
+ /**
+ * Initialize the execution
+ */
+ void initExecution() override;
+
+ /**
+ * Deinitialize the execution
+ */
+ void deinitExecution() override;
+
+ void setThreshold(float threshold);
+
+ void setLocalContrastAdaptationFactor(float factor);
+
+ bool determineDependingAreaOfInterest(rcti *input,
+ ReadBufferOperation *readOperation,
+ rcti *output) override;
+};
+
+/*-----------------------------------------------------------------------------*/
+/* Blending Weight Calculation (Second Pass) */
+
+class SMAABlendingWeightCalculationOperation : public NodeOperation {
+ private:
+ SocketReader *m_imageReader;
+
+ int m_corner_rounding;
+
+ public:
+ SMAABlendingWeightCalculationOperation();
+
+ /**
+ * the inner loop of this program
+ */
+ void executePixel(float output[4], int x, int y, void *data) override;
+
+ /**
+ * Initialize the execution
+ */
+ void initExecution() override;
+ void *initializeTileData(rcti *rect) override;
+
+ /**
+ * Deinitialize the execution
+ */
+ void deinitExecution() override;
+
+ void setCornerRounding(float rounding);
+
+ bool determineDependingAreaOfInterest(rcti *input,
+ ReadBufferOperation *readOperation,
+ rcti *output) override;
+
+ private:
+ /* Diagonal Search Functions */
+ int searchDiag1(int x, int y, int dir, bool *found);
+ int searchDiag2(int x, int y, int dir, bool *found);
+ void calculateDiagWeights(int x, int y, const float edges[2], float weights[2]);
+ bool isVerticalSearchUnneeded(int x, int y);
+
+ /* Horizontal/Vertical Search Functions */
+ int searchXLeft(int x, int y);
+ int searchXRight(int x, int y);
+ int searchYUp(int x, int y);
+ int searchYDown(int x, int y);
+
+ /* Corner Detection Functions */
+ void detectHorizontalCornerPattern(float weights[2], int left, int right, int y, int d1, int d2);
+ void detectVerticalCornerPattern(float weights[2], int x, int top, int bottom, int d1, int d2);
+};
+
+/*-----------------------------------------------------------------------------*/
+/* Neighborhood Blending (Third Pass) */
+
+class SMAANeighborhoodBlendingOperation : public NodeOperation {
+ private:
+ SocketReader *m_image1Reader;
+ SocketReader *m_image2Reader;
+
+ public:
+ SMAANeighborhoodBlendingOperation();
+
+ /**
+ * the inner loop of this program
+ */
+ void executePixel(float output[4], int x, int y, void *data) override;
+
+ /**
+ * Initialize the execution
+ */
+ void initExecution() override;
+ void *initializeTileData(rcti *rect) override;
+
+ /**
+ * Deinitialize the execution
+ */
+ void deinitExecution() override;
+
+ bool determineDependingAreaOfInterest(rcti *input,
+ ReadBufferOperation *readOperation,
+ rcti *output) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index b0c9bfb2663..03525d4ea01 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -18,6 +18,8 @@
#include "COM_ScaleOperation.h"
+namespace blender::compositor {
+
#define USE_FORCE_BILINEAR
/* XXX - ignore input and use default from old compositor,
* could become an option like the transform node - campbell
@@ -28,7 +30,7 @@
BaseScaleOperation::BaseScaleOperation()
{
#ifdef USE_FORCE_BILINEAR
- m_sampler = (int)COM_PS_BILINEAR;
+ m_sampler = (int)PixelSampler::Bilinear;
#else
m_sampler = -1;
#endif
@@ -89,8 +91,8 @@ bool ScaleOperation::determineDependingAreaOfInterest(rcti *input,
float scaleX[4];
float scaleY[4];
- this->m_inputXOperation->readSampled(scaleX, 0, 0, COM_PS_NEAREST);
- this->m_inputYOperation->readSampled(scaleY, 0, 0, COM_PS_NEAREST);
+ this->m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest);
+ this->m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest);
const float scx = scaleX[0];
const float scy = scaleY[0];
@@ -174,8 +176,8 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
float scaleX[4];
float scaleY[4];
- this->m_inputXOperation->readSampled(scaleX, 0, 0, COM_PS_NEAREST);
- this->m_inputYOperation->readSampled(scaleY, 0, 0, COM_PS_NEAREST);
+ this->m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest);
+ this->m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest);
const float scx = scaleX[0];
const float scy = scaleY[0];
@@ -203,7 +205,7 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
// Absolute fixed size
ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->m_inputOperation = nullptr;
@@ -308,3 +310,5 @@ void ScaleFixedSizeOperation::determineResolution(unsigned int resolution[2],
resolution[0] = this->m_newWidth;
resolution[1] = this->m_newHeight;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h
index 780107910f9..dc3de3602bf 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.h
+++ b/source/blender/compositor/operations/COM_ScaleOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class BaseScaleOperation : public NodeOperation {
public:
void setSampler(PixelSampler sampler)
@@ -129,3 +131,5 @@ class ScaleFixedSizeOperation : public BaseScaleOperation {
this->m_offsetY = y;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
index d5918dfa6f5..634fe66b0dd 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
@@ -24,13 +24,15 @@
#include "PIL_time.h"
+namespace blender::compositor {
+
ScreenLensDistortionOperation::ScreenLensDistortionOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
this->m_distortion = 0.0f;
this->m_dispersion = 0.0f;
@@ -83,12 +85,12 @@ void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/)
if (!m_distortion_const) {
float result[4];
- getInputSocketReader(1)->readSampled(result, 0, 0, COM_PS_NEAREST);
+ getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
m_distortion = result[0];
}
if (!m_dispersion_const) {
float result[4];
- getInputSocketReader(2)->readSampled(result, 0, 0, COM_PS_NEAREST);
+ getInputSocketReader(2)->readSampled(result, 0, 0, PixelSampler::Nearest);
m_dispersion = result[0];
}
@@ -351,3 +353,5 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp
mul_v3_v3fl(m_k4, m_k, 4.0f);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
index 793fc95dc3d..98872bfe142 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
@@ -21,6 +21,10 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+struct RNG;
+
+namespace blender::compositor {
+
class ScreenLensDistortionOperation : public NodeOperation {
private:
/**
@@ -96,3 +100,5 @@ class ScreenLensDistortionOperation : public NodeOperation {
float sum[4],
int count[3]) const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
index 3151bad5e4a..24edbc61d40 100644
--- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
+++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
@@ -18,6 +18,8 @@
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
SetAlphaMultiplyOperation::SetAlphaMultiplyOperation()
{
this->addInputSocket(DataType::Color);
@@ -53,3 +55,5 @@ void SetAlphaMultiplyOperation::deinitExecution()
this->m_inputColor = nullptr;
this->m_inputAlpha = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
index 094da1fd493..b4eea659fa2 100644
--- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
+++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* This operation will apply a mask to its input image.
*
@@ -38,3 +40,5 @@ class SetAlphaMultiplyOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
index cd9bf039f3e..90bfc814b09 100644
--- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
+++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
@@ -18,6 +18,8 @@
#include "COM_SetAlphaReplaceOperation.h"
+namespace blender::compositor {
+
SetAlphaReplaceOperation::SetAlphaReplaceOperation()
{
this->addInputSocket(DataType::Color);
@@ -51,3 +53,5 @@ void SetAlphaReplaceOperation::deinitExecution()
this->m_inputColor = nullptr;
this->m_inputAlpha = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
index 313d5f581eb..c84299b6d82 100644
--- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
+++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -43,3 +45,5 @@ class SetAlphaReplaceOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc
index b08381963fd..dbe45fa60db 100644
--- a/source/blender/compositor/operations/COM_SetColorOperation.cc
+++ b/source/blender/compositor/operations/COM_SetColorOperation.cc
@@ -18,9 +18,12 @@
#include "COM_SetColorOperation.h"
+namespace blender::compositor {
+
SetColorOperation::SetColorOperation()
{
this->addOutputSocket(DataType::Color);
+ flags.is_set_operation = true;
}
void SetColorOperation::executePixelSampled(float output[4],
@@ -37,3 +40,5 @@ void SetColorOperation::determineResolution(unsigned int resolution[2],
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h
index 147e76c433f..4b9b80013d4 100644
--- a/source/blender/compositor/operations/COM_SetColorOperation.h
+++ b/source/blender/compositor/operations/COM_SetColorOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -78,8 +80,6 @@ class SetColorOperation : public NodeOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
- bool isSetOperation() const override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.cc b/source/blender/compositor/operations/COM_SetSamplerOperation.cc
index 8272ad7583d..e68774736f3 100644
--- a/source/blender/compositor/operations/COM_SetSamplerOperation.cc
+++ b/source/blender/compositor/operations/COM_SetSamplerOperation.cc
@@ -18,6 +18,8 @@
#include "COM_SetSamplerOperation.h"
+namespace blender::compositor {
+
SetSamplerOperation::SetSamplerOperation()
{
this->addInputSocket(DataType::Color);
@@ -40,3 +42,5 @@ void SetSamplerOperation::executePixelSampled(float output[4],
{
this->m_reader->readSampled(output, x, y, this->m_sampler);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.h b/source/blender/compositor/operations/COM_SetSamplerOperation.h
index 4a0bd4563bb..d355d937806 100644
--- a/source/blender/compositor/operations/COM_SetSamplerOperation.h
+++ b/source/blender/compositor/operations/COM_SetSamplerOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output Sampler.
* it assumes we are in sRGB color space.
@@ -47,3 +49,5 @@ class SetSamplerOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc
index 98d0ad630ad..ef43cf64653 100644
--- a/source/blender/compositor/operations/COM_SetValueOperation.cc
+++ b/source/blender/compositor/operations/COM_SetValueOperation.cc
@@ -18,9 +18,12 @@
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
SetValueOperation::SetValueOperation()
{
this->addOutputSocket(DataType::Value);
+ flags.is_set_operation = true;
}
void SetValueOperation::executePixelSampled(float output[4],
@@ -37,3 +40,5 @@ void SetValueOperation::determineResolution(unsigned int resolution[2],
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h
index b3edb215010..5383f3b5fd3 100644
--- a/source/blender/compositor/operations/COM_SetValueOperation.h
+++ b/source/blender/compositor/operations/COM_SetValueOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -49,9 +51,6 @@ class SetValueOperation : public NodeOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
-
- bool isSetOperation() const override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc
index 6b6bcad02f3..7152d5e61d4 100644
--- a/source/blender/compositor/operations/COM_SetVectorOperation.cc
+++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc
@@ -19,9 +19,12 @@
#include "COM_SetVectorOperation.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
SetVectorOperation::SetVectorOperation()
{
this->addOutputSocket(DataType::Vector);
+ flags.is_set_operation = true;
}
void SetVectorOperation::executePixelSampled(float output[4],
@@ -40,3 +43,5 @@ void SetVectorOperation::determineResolution(unsigned int resolution[2],
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.h b/source/blender/compositor/operations/COM_SetVectorOperation.h
index a48584d7ded..b444339fcb2 100644
--- a/source/blender/compositor/operations/COM_SetVectorOperation.h
+++ b/source/blender/compositor/operations/COM_SetVectorOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -77,10 +79,6 @@ class SetVectorOperation : public NodeOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
- bool isSetOperation() const override
- {
- return true;
- }
void setVector(const float vector[3])
{
@@ -89,3 +87,5 @@ class SetVectorOperation : public NodeOperation {
setZ(vector[2]);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.cc b/source/blender/compositor/operations/COM_SocketProxyOperation.cc
index 53f5fea8795..39876439b7b 100644
--- a/source/blender/compositor/operations/COM_SocketProxyOperation.cc
+++ b/source/blender/compositor/operations/COM_SocketProxyOperation.cc
@@ -18,14 +18,19 @@
#include "COM_SocketProxyOperation.h"
+namespace blender::compositor {
+
SocketProxyOperation::SocketProxyOperation(DataType type, bool use_conversion)
- : m_use_conversion(use_conversion)
{
this->addInputSocket(type);
this->addOutputSocket(type);
+ flags.is_proxy_operation = true;
+ flags.use_datatype_conversion = use_conversion;
}
-std::unique_ptr<MetaData> SocketProxyOperation::getMetaData() const
+std::unique_ptr<MetaData> SocketProxyOperation::getMetaData()
{
return this->getInputSocket(0)->getReader()->getMetaData();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.h b/source/blender/compositor/operations/COM_SocketProxyOperation.h
index 712347a8ea2..1d3b76055bd 100644
--- a/source/blender/compositor/operations/COM_SocketProxyOperation.h
+++ b/source/blender/compositor/operations/COM_SocketProxyOperation.h
@@ -20,29 +20,13 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SocketProxyOperation : public NodeOperation {
public:
SocketProxyOperation(DataType type, bool use_conversion);
- bool isProxyOperation() const override
- {
- return true;
- }
- bool useDatatypeConversion() const override
- {
- return m_use_conversion;
- }
-
- bool getUseConversion() const
- {
- return m_use_conversion;
- }
- void setUseConversion(bool use_conversion)
- {
- m_use_conversion = use_conversion;
- }
- std::unique_ptr<MetaData> getMetaData() const override;
-
- private:
- bool m_use_conversion;
+ std::unique_ptr<MetaData> getMetaData() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc
index 25438259973..a4754de370d 100644
--- a/source/blender/compositor/operations/COM_SplitOperation.cc
+++ b/source/blender/compositor/operations/COM_SplitOperation.cc
@@ -27,6 +27,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
SplitOperation::SplitOperation()
{
this->addInputSocket(DataType::Color);
@@ -58,10 +60,10 @@ void SplitOperation::executePixelSampled(float output[4],
this->m_splitPercentage * this->getHeight() / 100.0f;
bool image1 = this->m_xSplit ? x > perc : y > perc;
if (image1) {
- this->m_image1Input->readSampled(output, x, y, COM_PS_NEAREST);
+ this->m_image1Input->readSampled(output, x, y, PixelSampler::Nearest);
}
else {
- this->m_image2Input->readSampled(output, x, y, COM_PS_NEAREST);
+ this->m_image2Input->readSampled(output, x, y, PixelSampler::Nearest);
}
}
@@ -76,3 +78,5 @@ void SplitOperation::determineResolution(unsigned int resolution[2],
NodeOperation::determineResolution(resolution, preferredResolution);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h
index ace5fd62eb2..09e48821dd0 100644
--- a/source/blender/compositor/operations/COM_SplitOperation.h
+++ b/source/blender/compositor/operations/COM_SplitOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SplitOperation : public NodeOperation {
private:
SocketReader *m_image1Input;
@@ -44,3 +46,5 @@ class SplitOperation : public NodeOperation {
this->m_xSplit = xsplit;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
index 23bf5897297..839eeb9ff8f 100644
--- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc
+++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
@@ -19,13 +19,15 @@
#include "COM_SunBeamsOperation.h"
+namespace blender::compositor {
+
SunBeamsOperation::SunBeamsOperation()
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
- this->setComplex(true);
+ this->flags.complex = true;
}
void SunBeamsOperation::initExecution()
@@ -138,7 +140,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f;
- float *iter = input->getBuffer() + COM_NUM_CHANNELS_COLOR * (x + input->getWidth() * y);
+ float *iter = input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * (x + input->getWidth() * y);
return iter;
}
@@ -167,7 +169,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) {
copy_v4_v4(output,
- input->getBuffer() + COM_NUM_CHANNELS_COLOR *
+ input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS *
((int)source[0] + input->getWidth() * (int)source[1]));
return;
}
@@ -208,7 +210,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
/* decrement u */
x -= fxu;
y -= fyu;
- buffer -= (fxu + fyu * buffer_width) * COM_NUM_CHANNELS_COLOR;
+ buffer -= (fxu + fyu * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS;
/* decrement v (in steps of dv < 1) */
v_local -= dv;
@@ -217,7 +219,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
x -= fxv;
y -= fyv;
- buffer -= (fxv + fyv * buffer_width) * COM_NUM_CHANNELS_COLOR;
+ buffer -= (fxv + fyv * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS;
}
}
@@ -353,3 +355,5 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h
index 7cf5cf9971e..d3725021cde 100644
--- a/source/blender/compositor/operations/COM_SunBeamsOperation.h
+++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h
@@ -19,6 +19,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SunBeamsOperation : public NodeOperation {
public:
SunBeamsOperation();
@@ -44,3 +46,5 @@ class SunBeamsOperation : public NodeOperation {
float m_source_px[2];
float m_ray_length_px;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc
index 146f43dbe3a..059a289ae4d 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.cc
+++ b/source/blender/compositor/operations/COM_TextureOperation.cc
@@ -25,6 +25,8 @@
#include "BLI_listbase.h"
#include "BLI_threads.h"
+namespace blender::compositor {
+
TextureBaseOperation::TextureBaseOperation()
{
this->addInputSocket(DataType::Vector); // offset
@@ -35,7 +37,7 @@ TextureBaseOperation::TextureBaseOperation()
this->m_rd = nullptr;
this->m_pool = nullptr;
this->m_sceneColorManage = false;
- setComplex(true);
+ flags.complex = true;
}
TextureOperation::TextureOperation() : TextureBaseOperation()
{
@@ -73,15 +75,31 @@ void TextureBaseOperation::deinitExecution()
void TextureBaseOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
- if (preferredResolution[0] == 0 || preferredResolution[1] == 0) {
- int width = this->m_rd->xsch * this->m_rd->size / 100;
- int height = this->m_rd->ysch * this->m_rd->size / 100;
- resolution[0] = width;
- resolution[1] = height;
- }
- else {
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
+ switch (execution_model_) {
+ case eExecutionModel::Tiled: {
+ if (preferredResolution[0] == 0 || preferredResolution[1] == 0) {
+ int width = this->m_rd->xsch * this->m_rd->size / 100;
+ int height = this->m_rd->ysch * this->m_rd->size / 100;
+ resolution[0] = width;
+ resolution[1] = height;
+ }
+ else {
+ resolution[0] = preferredResolution[0];
+ resolution[1] = preferredResolution[1];
+ }
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ /* Determine inputs resolutions. */
+ unsigned int temp[2];
+ NodeOperation::determineResolution(temp, preferredResolution);
+
+ /* We don't use inputs resolutions because they are only used as parameters, not image data.
+ */
+ resolution[0] = preferredResolution[0];
+ resolution[1] = preferredResolution[1];
+ break;
+ }
}
}
@@ -155,3 +173,5 @@ void TextureBaseOperation::executePixelSampled(float output[4],
output[0] = output[1] = output[2] = output[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h
index 1a6e005f752..e5f56673694 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.h
+++ b/source/blender/compositor/operations/COM_TextureOperation.h
@@ -26,6 +26,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+namespace blender::compositor {
+
/**
* Base class for all renderlayeroperations
*
@@ -42,7 +44,7 @@ class TextureBaseOperation : public NodeOperation {
protected:
/**
- * Determine the output resolution. The resolution is retrieved from the Renderer
+ * Determine the output resolution.
*/
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
@@ -80,3 +82,5 @@ class TextureAlphaOperation : public TextureBaseOperation {
TextureAlphaOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc
index 85011171432..6bfacb0c75d 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.cc
+++ b/source/blender/compositor/operations/COM_TonemapOperation.cc
@@ -22,14 +22,16 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
TonemapOperation::TonemapOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->m_imageReader = nullptr;
this->m_data = nullptr;
this->m_cachedInstance = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void TonemapOperation::initExecution()
{
@@ -150,3 +152,5 @@ void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/)
{
/* pass */
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h
index e7da983fe61..7ecb179504d 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.h
+++ b/source/blender/compositor/operations/COM_TonemapOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief temporarily storage during execution of Tone-map
* \ingroup operation
@@ -98,3 +100,5 @@ class PhotoreceptorTonemapOperation : public TonemapOperation {
*/
void executePixel(float output[4], int x, int y, void *data) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc
index 97d602aa458..993410e3e84 100644
--- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc
+++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc
@@ -28,6 +28,8 @@
#include "BKE_node.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
TrackPositionOperation::TrackPositionOperation()
{
this->addOutputSocket(DataType::Value);
@@ -39,6 +41,7 @@ TrackPositionOperation::TrackPositionOperation()
this->m_position = CMP_TRACKPOS_ABSOLUTE;
this->m_relativeFrame = 0;
this->m_speed_output = false;
+ flags.is_set_operation = true;
}
void TrackPositionOperation::initExecution()
@@ -134,3 +137,5 @@ void TrackPositionOperation::determineResolution(unsigned int resolution[2],
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h
index 2e2c1f36c52..b0b0a123bd6 100644
--- a/source/blender/compositor/operations/COM_TrackPositionOperation.h
+++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h
@@ -28,6 +28,8 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+namespace blender::compositor {
+
/**
* Class with implementation of green screen gradient rasterization
*/
@@ -91,9 +93,6 @@ class TrackPositionOperation : public NodeOperation {
void initExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
-
- bool isSetOperation() const override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc
index 7efd655b1df..49135f25320 100644
--- a/source/blender/compositor/operations/COM_TranslateOperation.cc
+++ b/source/blender/compositor/operations/COM_TranslateOperation.cc
@@ -18,6 +18,8 @@
#include "COM_TranslateOperation.h"
+namespace blender::compositor {
+
TranslateOperation::TranslateOperation()
{
this->addInputSocket(DataType::Color);
@@ -56,7 +58,7 @@ void TranslateOperation::executePixelSampled(float output[4],
float originalXPos = x - this->getDeltaX();
float originalYPos = y - this->getDeltaY();
- this->m_inputOperation->readSampled(output, originalXPos, originalYPos, COM_PS_BILINEAR);
+ this->m_inputOperation->readSampled(output, originalXPos, originalYPos, PixelSampler::Bilinear);
}
bool TranslateOperation::determineDependingAreaOfInterest(rcti *input,
@@ -80,3 +82,5 @@ void TranslateOperation::setFactorXY(float factorX, float factorY)
m_factorX = factorX;
m_factorY = factorY;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h
index d55cc9096c0..eb3a664159f 100644
--- a/source/blender/compositor/operations/COM_TranslateOperation.h
+++ b/source/blender/compositor/operations/COM_TranslateOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class TranslateOperation : public NodeOperation {
private:
SocketReader *m_inputOperation;
@@ -54,9 +56,9 @@ class TranslateOperation : public NodeOperation {
{
if (!this->m_isDeltaSet) {
float tempDelta[4];
- this->m_inputXOperation->readSampled(tempDelta, 0, 0, COM_PS_NEAREST);
+ this->m_inputXOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest);
this->m_deltaX = tempDelta[0];
- this->m_inputYOperation->readSampled(tempDelta, 0, 0, COM_PS_NEAREST);
+ this->m_inputYOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest);
this->m_deltaY = tempDelta[0];
this->m_isDeltaSet = true;
}
@@ -64,3 +66,5 @@ class TranslateOperation : public NodeOperation {
void setFactorXY(float factorX, float factorY);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
index ea33f3cd787..19cd5a53084 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
@@ -22,18 +22,20 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation()
{
this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE); // do not resize the bokeh image.
+ this->addInputSocket(DataType::Color, ResizeMode::None); // do not resize the bokeh image.
this->addInputSocket(DataType::Value); // radius
#ifdef COM_DEFOCUS_SEARCH
this->addInputSocket(DataType::Color,
- COM_SC_NO_RESIZE); // inverse search radius optimization structure.
+ ResizeMode::None); // inverse search radius optimization structure.
#endif
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
- this->setOpenCL(true);
+ flags.complex = true;
+ flags.open_cl = true;
this->m_inputProgram = nullptr;
this->m_inputBokehProgram = nullptr;
@@ -135,14 +137,14 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y,
const int addXStepValue = QualityStepHelper::getStep();
const int addYStepValue = addXStepValue;
- const int addXStepColor = addXStepValue * COM_NUM_CHANNELS_COLOR;
+ const int addXStepColor = addXStepValue * COM_DATA_TYPE_COLOR_CHANNELS;
if (size_center > this->m_threshold) {
for (int ny = miny; ny < maxy; ny += addYStepValue) {
float dy = ny - y;
int offsetValueNy = ny * inputSizeBuffer->getWidth();
int offsetValueNxNy = offsetValueNy + (minx);
- int offsetColorNxNy = offsetValueNxNy * COM_NUM_CHANNELS_COLOR;
+ int offsetColorNxNy = offsetValueNxNy * COM_DATA_TYPE_COLOR_CHANNELS;
for (int nx = minx; nx < maxx; nx += addXStepValue) {
if (nx != x || ny != y) {
float size = MIN2(inputSizeFloatBuffer[offsetValueNxNy] * scalar, size_center);
@@ -278,9 +280,9 @@ bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest(
// InverseSearchRadiusOperation
InverseSearchRadiusOperation::InverseSearchRadiusOperation()
{
- this->addInputSocket(DataType::Value, COM_SC_NO_RESIZE); // radius
+ this->addInputSocket(DataType::Value, ResizeMode::None); // radius
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputRadius = nullptr;
}
@@ -319,7 +321,7 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect)
// for (int x2 = 0 ; x2 < DIVIDER ; x2 ++) {
// for (int y2 = 0 ; y2 < DIVIDER ; y2 ++) {
- // this->m_inputRadius->read(temp, rx+x2, ry+y2, COM_PS_NEAREST);
+ // this->m_inputRadius->read(temp, rx+x2, ry+y2, PixelSampler::Nearest);
// if (radius < temp[0]) {
// radius = temp[0];
// maxx = x2;
@@ -381,3 +383,5 @@ bool InverseSearchRadiusOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newRect, readOperation, output);
}
#endif
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
index 5b859e5a2fd..baeab6a646e 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
//#define COM_DEFOCUS_SEARCH
class VariableSizeBokehBlurOperation : public NodeOperation, public QualityStepHelper {
@@ -124,3 +126,5 @@ class InverseSearchRadiusOperation : public NodeOperation {
}
};
#endif
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc
index ff9eef8a7e1..fd64bda156b 100644
--- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc
@@ -25,6 +25,8 @@
#include "COM_VectorBlurOperation.h"
+namespace blender::compositor {
+
/* Defined */
#define PASS_VECTOR_MAX 10000.0f
@@ -54,7 +56,7 @@ VectorBlurOperation::VectorBlurOperation()
this->m_inputImageProgram = nullptr;
this->m_inputSpeedProgram = nullptr;
this->m_inputZProgram = nullptr;
- setComplex(true);
+ flags.complex = true;
}
void VectorBlurOperation::initExecution()
{
@@ -69,7 +71,7 @@ void VectorBlurOperation::initExecution()
void VectorBlurOperation::executePixel(float output[4], int x, int y, void *data)
{
float *buffer = (float *)data;
- int index = (y * this->getWidth() + x) * COM_NUM_CHANNELS_COLOR;
+ int index = (y * this->getWidth() + x) * COM_DATA_TYPE_COLOR_CHANNELS;
copy_v4_v4(output, &buffer[index]);
}
@@ -897,3 +899,5 @@ void zbuf_accumulate_vecblur(NodeBlurData *nbd,
}
zbuf_free_span(&zspan);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h
index 10affb48f20..dfcf1fb16f7 100644
--- a/source/blender/compositor/operations/COM_VectorBlurOperation.h
+++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h
@@ -22,6 +22,8 @@
#include "COM_QualityStepHelper.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class VectorBlurOperation : public NodeOperation, public QualityStepHelper {
private:
/**
@@ -72,3 +74,5 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper {
MemoryBuffer *inputSpeed,
MemoryBuffer *inputZ);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc
index a6638a78e88..9d53ed5d8ee 100644
--- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc
+++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc
@@ -20,6 +20,8 @@
#include "BKE_colortools.h"
+namespace blender::compositor {
+
VectorCurveOperation::VectorCurveOperation()
{
this->addInputSocket(DataType::Vector);
@@ -50,3 +52,5 @@ void VectorCurveOperation::deinitExecution()
CurveBaseOperation::deinitExecution();
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h
index 0c164a1f3c8..8cbb80e27c7 100644
--- a/source/blender/compositor/operations/COM_VectorCurveOperation.h
+++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h
@@ -21,6 +21,8 @@
#include "COM_CurveBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class VectorCurveOperation : public CurveBaseOperation {
private:
/**
@@ -46,3 +48,5 @@ class VectorCurveOperation : public CurveBaseOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc
index ea5937d8afb..860f56e23fa 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.cc
+++ b/source/blender/compositor/operations/COM_ViewerOperation.cc
@@ -32,6 +32,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
ViewerOperation::ViewerOperation()
{
this->setImage(nullptr);
@@ -53,6 +55,8 @@ ViewerOperation::ViewerOperation()
this->m_depthInput = nullptr;
this->m_rd = nullptr;
this->m_viewName = nullptr;
+ flags.use_viewer_border = true;
+ flags.is_viewer_operation = true;
}
void ViewerOperation::initExecution()
@@ -98,12 +102,12 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
for (y = y1; y < y2 && (!breaked); y++) {
for (x = x1; x < x2; x++) {
- this->m_imageInput->readSampled(&(buffer[offset4]), x, y, COM_PS_NEAREST);
+ this->m_imageInput->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest);
if (this->m_useAlphaInput) {
- this->m_alphaInput->readSampled(alpha, x, y, COM_PS_NEAREST);
+ this->m_alphaInput->readSampled(alpha, x, y, PixelSampler::Nearest);
buffer[offset4 + 3] = alpha[0];
}
- this->m_depthInput->readSampled(depth, x, y, COM_PS_NEAREST);
+ this->m_depthInput->readSampled(depth, x, y, PixelSampler::Nearest);
depthbuffer[offset] = depth[0];
offset++;
@@ -205,11 +209,13 @@ void ViewerOperation::updateImage(rcti *rect)
this->updateDraw();
}
-CompositorPriority ViewerOperation::getRenderPriority() const
+eCompositorPriority ViewerOperation::getRenderPriority() const
{
if (this->isActiveViewerOutput()) {
- return CompositorPriority::High;
+ return eCompositorPriority::High;
}
- return CompositorPriority::Low;
+ return eCompositorPriority::Low;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h
index a3df19d5e90..c0f13ff79fc 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.h
+++ b/source/blender/compositor/operations/COM_ViewerOperation.h
@@ -23,6 +23,8 @@
#include "COM_NodeOperation.h"
#include "DNA_image_types.h"
+namespace blender::compositor {
+
class ViewerOperation : public NodeOperation {
private:
float *m_outputBuffer;
@@ -100,11 +102,7 @@ class ViewerOperation : public NodeOperation {
{
return this->m_chunkOrder;
}
- CompositorPriority getRenderPriority() const override;
- bool isViewerOperation() const override
- {
- return true;
- }
+ eCompositorPriority getRenderPriority() const override;
void setUseAlphaInput(bool value)
{
this->m_useAlphaInput = value;
@@ -131,3 +129,5 @@ class ViewerOperation : public NodeOperation {
void updateImage(rcti *rect);
void initImage();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc
index a869666967e..d0d2fcac3ac 100644
--- a/source/blender/compositor/operations/COM_WrapOperation.cc
+++ b/source/blender/compositor/operations/COM_WrapOperation.cc
@@ -20,6 +20,8 @@
#include "COM_WrapOperation.h"
+namespace blender::compositor {
+
WrapOperation::WrapOperation(DataType datatype) : ReadBufferOperation(datatype)
{
this->m_wrappingType = CMP_NODE_WRAP_NONE;
@@ -115,3 +117,5 @@ void WrapOperation::setWrapping(int wrapping_type)
{
m_wrappingType = wrapping_type;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WrapOperation.h b/source/blender/compositor/operations/COM_WrapOperation.h
index 21123ed490c..6279129a550 100644
--- a/source/blender/compositor/operations/COM_WrapOperation.h
+++ b/source/blender/compositor/operations/COM_WrapOperation.h
@@ -20,6 +20,8 @@
#include "COM_ReadBufferOperation.h"
+namespace blender::compositor {
+
class WrapOperation : public ReadBufferOperation {
private:
int m_wrappingType;
@@ -37,3 +39,5 @@ class WrapOperation : public ReadBufferOperation {
void setFactorXY(float factorX, float factorY);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc
index e426bc76ef3..1aa19f26e2b 100644
--- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc
+++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc
@@ -21,12 +21,15 @@
#include "COM_defines.h"
#include <cstdio>
+namespace blender::compositor {
+
WriteBufferOperation::WriteBufferOperation(DataType datatype)
{
this->addInputSocket(datatype);
this->m_memoryProxy = new MemoryProxy(datatype);
this->m_memoryProxy->setWriteBufferOperation(this);
this->m_memoryProxy->setExecutor(nullptr);
+ flags.is_write_buffer_operation = true;
}
WriteBufferOperation::~WriteBufferOperation()
{
@@ -61,7 +64,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/
MemoryBuffer *memoryBuffer = this->m_memoryProxy->getBuffer();
float *buffer = memoryBuffer->getBuffer();
const uint8_t num_channels = memoryBuffer->get_num_channels();
- if (this->m_input->isComplex()) {
+ if (this->m_input->get_flags().complex) {
void *data = this->m_input->initializeTileData(rect);
int x1 = rect->xmin;
int y1 = rect->ymin;
@@ -97,7 +100,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/
for (y = y1; y < y2 && (!breaked); y++) {
int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels;
for (x = x1; x < x2; x++) {
- this->m_input->readSampled(&(buffer[offset4]), x, y, COM_PS_NEAREST);
+ this->m_input->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest);
offset4 += num_channels;
}
if (isBraked()) {
@@ -225,3 +228,5 @@ void WriteBufferOperation::readResolutionFromInputSocket()
this->setWidth(inputOperation->getWidth());
this->setHeight(inputOperation->getHeight());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h
index 00d48e38fca..2817fbe24b9 100644
--- a/source/blender/compositor/operations/COM_WriteBufferOperation.h
+++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h
@@ -20,7 +20,12 @@
#include "COM_MemoryProxy.h"
#include "COM_NodeOperation.h"
-#include "COM_SocketReader.h"
+
+namespace blender::compositor {
+
+class OpenCLDevice;
+class MemoryProxy;
+
/**
* \brief NodeOperation to write to a tile
* \ingroup Operation
@@ -38,10 +43,6 @@ class WriteBufferOperation : public NodeOperation {
return this->m_memoryProxy;
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- bool isWriteBufferOperation() const override
- {
- return true;
- }
bool isSingleValue() const
{
return m_single_value;
@@ -63,3 +64,5 @@ class WriteBufferOperation : public NodeOperation {
return m_input;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc
index 8afdbcc7c2d..9d3ca7e736e 100644
--- a/source/blender/compositor/operations/COM_ZCombineOperation.cc
+++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ZCombineOperation.h"
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
ZCombineOperation::ZCombineOperation()
{
this->addInputSocket(DataType::Color);
@@ -158,3 +160,5 @@ void ZCombineMaskOperation::deinitExecution()
this->m_maskReader = nullptr;
this->m_image2Reader = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h
index 91044d44985..d0b1aee7310 100644
--- a/source/blender/compositor/operations/COM_ZCombineOperation.h
+++ b/source/blender/compositor/operations/COM_ZCombineOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -66,3 +68,5 @@ class ZCombineMaskOperation : public NodeOperation {
class ZCombineMaskAlphaOperation : public ZCombineMaskOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c
index f4f510891e0..7f1d90f20ea 100644
--- a/source/blender/datatoc/datatoc_icon.c
+++ b/source/blender/datatoc/datatoc_icon.c
@@ -66,9 +66,9 @@ static bool path_test_extension(const char *str, const char *ext)
return !(a == 0 || b == 0 || b >= a) && (strcmp(ext, str + a - b) == 0);
}
-static void endian_switch_uint32(unsigned int *val)
+static void endian_switch_uint32(uint *val)
{
- unsigned int tval = *val;
+ uint tval = *val;
*val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24));
}
@@ -96,10 +96,7 @@ static const char *path_basename(const char *path)
/* -------------------------------------------------------------------- */
/* Write a PNG from RGBA pixels */
-static bool write_png(const char *name,
- const unsigned int *pixels,
- const int width,
- const int height)
+static bool write_png(const char *name, const uint *pixels, const int width, const int height)
{
png_structp png_ptr;
png_infop info_ptr;
@@ -199,9 +196,9 @@ static bool write_png(const char *name,
/* Merge icon-data from files */
struct IconHead {
- unsigned int icon_w, icon_h;
- unsigned int orig_x, orig_y;
- unsigned int canvas_w, canvas_h;
+ uint icon_w, icon_h;
+ uint orig_x, orig_y;
+ uint canvas_w, canvas_h;
};
struct IconInfo {
@@ -289,10 +286,10 @@ static bool icon_decode_head(FILE *f_src, struct IconHead *r_head)
return false;
}
-static bool icon_decode(FILE *f_src, struct IconHead *r_head, unsigned int **r_pixels)
+static bool icon_decode(FILE *f_src, struct IconHead *r_head, uint **r_pixels)
{
- unsigned int *pixels;
- unsigned int pixels_size;
+ uint *pixels;
+ uint pixels_size;
if (!icon_decode_head(f_src, r_head)) {
printf("%s: failed to read header\n", __func__);
@@ -316,7 +313,7 @@ static bool icon_decode(FILE *f_src, struct IconHead *r_head, unsigned int **r_p
return true;
}
-static bool icon_read(const char *file_src, struct IconHead *r_head, unsigned int **r_pixels)
+static bool icon_read(const char *file_src, struct IconHead *r_head, uint **r_pixels)
{
FILE *f_src;
bool success;
@@ -335,18 +332,18 @@ static bool icon_read(const char *file_src, struct IconHead *r_head, unsigned in
static bool icon_merge(struct IconMergeContext *context,
const char *file_src,
- unsigned int **r_pixels_canvas,
- unsigned int *r_canvas_w,
- unsigned int *r_canvas_h)
+ uint32_t **r_pixels_canvas,
+ uint *r_canvas_w,
+ uint *r_canvas_h)
{
struct IconHead head;
- unsigned int *pixels;
+ uint *pixels;
- unsigned int x, y;
+ uint x, y;
/* canvas */
- unsigned int *pixels_canvas;
- unsigned int canvas_w, canvas_h;
+ uint32_t *pixels_canvas;
+ uint canvas_w, canvas_h;
if (!icon_read(file_src, &head, &pixels)) {
return false;
@@ -365,7 +362,7 @@ static bool icon_merge(struct IconMergeContext *context,
/* init once */
*r_canvas_w = head.canvas_w;
*r_canvas_h = head.canvas_h;
- *r_pixels_canvas = calloc(1, (head.canvas_w * head.canvas_h) * sizeof(const unsigned char[4]));
+ *r_pixels_canvas = calloc(1, (head.canvas_w * head.canvas_h) * sizeof(uint32_t));
}
canvas_w = *r_canvas_w;
@@ -377,9 +374,9 @@ static bool icon_merge(struct IconMergeContext *context,
for (x = 0; x < head.icon_w; x++) {
for (y = 0; y < head.icon_h; y++) {
- unsigned int pixel;
- unsigned int dst_x, dst_y;
- unsigned int pixel_xy_dst;
+ uint pixel;
+ uint dst_x, dst_y;
+ uint pixel_xy_dst;
/* get pixel */
pixel = pixels[(y * head.icon_w) + x];
@@ -413,8 +410,8 @@ static bool icondir_to_png(const char *path_src, const char *file_dst)
struct IconMergeContext context;
- unsigned int *pixels_canvas = NULL;
- unsigned int canvas_w = 0, canvas_h = 0;
+ uint32_t *pixels_canvas = NULL;
+ uint canvas_w = 0, canvas_h = 0;
icon_merge_context_init(&context);
diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h
index 567916fdebe..740124f6113 100644
--- a/source/blender/depsgraph/DEG_depsgraph.h
+++ b/source/blender/depsgraph/DEG_depsgraph.h
@@ -139,16 +139,19 @@ void DEG_graph_time_tag_update(struct Depsgraph *depsgraph);
void DEG_graph_id_type_tag(struct Depsgraph *depsgraph, short id_type);
void DEG_id_type_tag(struct Main *bmain, short id_type);
-void DEG_ids_clear_recalc(struct Main *bmain, Depsgraph *depsgraph);
+/* Set a depsgraph to flush updates to editors. This would be done
+ * for viewport depsgraphs, but not render or export depsgraph for example. */
+void DEG_enable_editors_update(struct Depsgraph *depsgraph);
-/* Check if something was changed in the database and inform
- * editors about this.
- */
-void DEG_ids_check_recalc(struct Main *bmain,
- struct Depsgraph *depsgraph,
- struct Scene *scene,
- struct ViewLayer *view_layer,
- bool time);
+/* Check if something was changed in the database and inform editors about this. */
+void DEG_editors_update(struct Depsgraph *depsgraph, bool time);
+
+/* Clear recalc flags after editors or renderers have handled updates. */
+void DEG_ids_clear_recalc(Depsgraph *depsgraph, const bool backup);
+
+/* Restore recalc flags, backed up by a previous call to DEG_ids_clear_recalc.
+ * This also clears the backup. */
+void DEG_ids_restore_recalc(Depsgraph *depsgraph);
/* ************************************************ */
/* Evaluation Engine API */
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 4e618d8625d..b4acf9b010c 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -40,6 +40,7 @@ struct Object;
struct Scene;
struct Simulation;
struct bNodeTree;
+struct Collection;
#include "BLI_sys_types.h"
@@ -137,6 +138,12 @@ void DEG_add_object_relation(struct DepsNodeHandle *node_handle,
struct Object *object,
eDepsObjectComponentType component,
const char *description);
+void DEG_add_collection_geometry_relation(struct DepsNodeHandle *node_handle,
+ struct Collection *collection,
+ const char *description);
+void DEG_add_collection_geometry_customdata_mask(struct DepsNodeHandle *node_handle,
+ struct Collection *collection,
+ const struct CustomData_MeshMasks *masks);
void DEG_add_simulation_relation(struct DepsNodeHandle *node_handle,
struct Simulation *simulation,
const char *description);
@@ -182,6 +189,8 @@ void DEG_add_customdata_mask(struct DepsNodeHandle *handle,
struct ID *DEG_get_id_from_handle(struct DepsNodeHandle *node_handle);
struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle);
+bool DEG_object_has_geometry_component(struct Object *object);
+
/* ************************************************ */
#ifdef __cplusplus
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index e4660c34762..f4d65698bee 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -77,10 +77,6 @@ DepsgraphBuilder::DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuild
{
}
-DepsgraphBuilder::~DepsgraphBuilder()
-{
-}
-
bool DepsgraphBuilder::need_pull_base_into_graph(Base *base)
{
/* Simple check: enabled bases are always part of dependency graph. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h
index 36b6b1bf17d..6e1c8d8526f 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder.h
@@ -37,7 +37,7 @@ class DepsgraphBuilderCache;
class DepsgraphBuilder {
public:
- virtual ~DepsgraphBuilder();
+ virtual ~DepsgraphBuilder() = default;
virtual bool need_pull_base_into_graph(Base *base);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
index df108072142..af7717d7595 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
@@ -149,10 +149,6 @@ bool AnimatedPropertyStorage::isPropertyAnimated(const PointerRNA *pointer_rna,
/* Builder cache itself. */
-DepsgraphBuilderCache::DepsgraphBuilderCache()
-{
-}
-
DepsgraphBuilderCache::~DepsgraphBuilderCache()
{
for (AnimatedPropertyStorage *animated_property_storage :
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.h b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
index e04ae3a3727..c955a22a5cf 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
@@ -81,7 +81,6 @@ class AnimatedPropertyStorage {
/* Cached data which can be re-used by multiple builders. */
class DepsgraphBuilderCache {
public:
- DepsgraphBuilderCache();
~DepsgraphBuilderCache();
/* Makes sure storage for animated properties exists and initialized for the given ID. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.cc b/source/blender/depsgraph/intern/builder/deg_builder_map.cc
index 5da54350cfc..6e926da6b29 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_map.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_map.cc
@@ -27,14 +27,6 @@
namespace blender::deg {
-BuilderMap::BuilderMap()
-{
-}
-
-BuilderMap::~BuilderMap()
-{
-}
-
bool BuilderMap::checkIsBuilt(ID *id, int tag) const
{
return (getIDTag(id) & tag) == tag;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.h b/source/blender/depsgraph/intern/builder/deg_builder_map.h
index 8b23d3d0d3b..53f6a722e85 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_map.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_map.h
@@ -47,9 +47,6 @@ class BuilderMap {
TAG_SCENE_COMPOSITOR | TAG_SCENE_SEQUENCER | TAG_SCENE_AUDIO),
};
- BuilderMap();
- ~BuilderMap();
-
/* Check whether given ID is already handled by builder (or if it's being handled). */
bool checkIsBuilt(ID *id, int tag = TAG_COMPLETE) const;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index d8dc66883a0..ae530cc010e 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -84,6 +84,7 @@
#include "BKE_lattice.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
#include "BKE_light.h"
#include "BKE_mask.h"
#include "BKE_material.h"
@@ -114,6 +115,7 @@
#include "intern/builder/deg_builder.h"
#include "intern/depsgraph.h"
+#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/node/deg_node.h"
@@ -360,7 +362,103 @@ void DepsgraphNodeBuilder::begin_build()
graph_->entry_tags.clear();
}
-void DepsgraphNodeBuilder::end_build()
+/* Util callbacks for `BKE_library_foreach_ID_link`, used to detect when a COW ID is using ID
+ * pointers that are either:
+ * - COW ID pointers that do not exist anymore in current depsgraph.
+ * - Orig ID pointers that do have now a COW version in current depsgraph.
+ * In both cases, it means the COW ID user needs to be flushed, to ensure its pointers are properly
+ * remapped.
+ *
+ * NOTE: This is split in two, a static function and a public method of the node builder, to allow
+ * the code to access the builder's data more easily. */
+
+/* `id_cow_self` is the user of `id_pointer`, see also `LibraryIDLinkCallbackData` struct
+ * definition. */
+int DepsgraphNodeBuilder::foreach_id_cow_detect_need_for_update_callback(ID *id_cow_self,
+ ID *id_pointer)
+{
+ if (id_pointer->orig_id == nullptr) {
+ /* `id_cow_self` uses a non-cow ID, if that ID has a COW copy in current depsgraph its owner
+ * needs to be remapped, i.e. COW-flushed. */
+ IDNode *id_node = find_id_node(id_pointer);
+ if (id_node != nullptr && id_node->id_cow != nullptr) {
+ graph_id_tag_update(bmain_,
+ graph_,
+ id_cow_self->orig_id,
+ ID_RECALC_COPY_ON_WRITE,
+ DEG_UPDATE_SOURCE_RELATIONS);
+ return IDWALK_RET_STOP_ITER;
+ }
+ }
+ else {
+ /* `id_cow_self` uses a COW ID, if that COW copy is removed from current depsgraph its owner
+ * needs to be remapped, i.e. COW-flushed. */
+ /* NOTE: at that stage, old existing COW copies that are to be removed from current state of
+ * evaluated depsgraph are still valid pointers, they are freed later (typically during
+ * destruction of the builder itself). */
+ IDNode *id_node = find_id_node(id_pointer->orig_id);
+ if (id_node == nullptr) {
+ graph_id_tag_update(bmain_,
+ graph_,
+ id_cow_self->orig_id,
+ ID_RECALC_COPY_ON_WRITE,
+ DEG_UPDATE_SOURCE_RELATIONS);
+ return IDWALK_RET_STOP_ITER;
+ }
+ }
+ return IDWALK_RET_NOP;
+}
+
+static int foreach_id_cow_detect_need_for_update_callback(LibraryIDLinkCallbackData *cb_data)
+{
+ ID *id = *cb_data->id_pointer;
+ if (id == nullptr) {
+ return IDWALK_RET_NOP;
+ }
+
+ DepsgraphNodeBuilder *builder = static_cast<DepsgraphNodeBuilder *>(cb_data->user_data);
+ ID *id_cow_self = cb_data->id_self;
+
+ return builder->foreach_id_cow_detect_need_for_update_callback(id_cow_self, id);
+}
+
+/* Check for IDs that need to be flushed (COW-updated) because the depsgraph itself created or
+ * removed some of their evaluated dependencies.
+ *
+ * NOTE: Currently the only ID types that depsgraph may decide to not evaluate/generate COW
+ * copies for, even though they are referenced by other data-blocks, are Collections and Objects
+ * (through their various visibility flags, and the ones from LayerCollections too). However, this
+ * code is kept generic as it makes it more future-proof, and optimization here would give
+ * negligible performance improvements in typical cases.
+ *
+ * NOTE: This mechanism may also 'fix' some missing update tagging from non-depsgraph code in
+ * some cases. This is slightly unfortunate (as it may hide issues in other parts of Blender
+ * code), but cannot really be avoided currently.
+ */
+void DepsgraphNodeBuilder::update_invalid_cow_pointers()
+{
+ for (const IDNode *id_node : graph_->id_nodes) {
+ if (id_node->previously_visible_components_mask == 0) {
+ /* Newly added node/ID, no need to check it. */
+ continue;
+ }
+ if (ELEM(id_node->id_cow, id_node->id_orig, nullptr)) {
+ /* Node/ID with no COW data, no need to check it. */
+ continue;
+ }
+ if ((id_node->id_cow->recalc & ID_RECALC_COPY_ON_WRITE) != 0) {
+ /* Node/ID already tagged for COW flush, no need to check it. */
+ continue;
+ }
+ BKE_library_foreach_ID_link(nullptr,
+ id_node->id_cow,
+ deg::foreach_id_cow_detect_need_for_update_callback,
+ this,
+ IDWALK_IGNORE_EMBEDDED_ID | IDWALK_READONLY);
+ }
+}
+
+void DepsgraphNodeBuilder::tag_previously_tagged_nodes()
{
for (const SavedEntryTag &entry_tag : saved_entry_tags_) {
IDNode *id_node = find_id_node(entry_tag.id_orig);
@@ -382,6 +480,12 @@ void DepsgraphNodeBuilder::end_build()
}
}
+void DepsgraphNodeBuilder::end_build()
+{
+ tag_previously_tagged_nodes();
+ update_invalid_cow_pointers();
+}
+
void DepsgraphNodeBuilder::build_id(ID *id)
{
if (id == nullptr) {
@@ -554,6 +658,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
id_node->is_directly_visible = is_collection_visible;
build_idproperties(collection->id.properties);
+ add_operation_node(&collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE);
}
if (from_layer_collection != nullptr) {
/* If we came from layer collection we don't go deeper, view layer
@@ -1556,6 +1661,12 @@ void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket)
else if (socket->type == SOCK_COLLECTION) {
build_id((ID *)((bNodeSocketValueCollection *)socket->default_value)->value);
}
+ else if (socket->type == SOCK_TEXTURE) {
+ build_id((ID *)((bNodeSocketValueTexture *)socket->default_value)->value);
+ }
+ else if (socket->type == SOCK_MATERIAL) {
+ build_id((ID *)((bNodeSocketValueMaterial *)socket->default_value)->value);
+ }
}
void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index a7033c8c8f3..151e0d844b6 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -101,6 +101,8 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void begin_build();
virtual void end_build();
+ int foreach_id_cow_detect_need_for_update_callback(ID *id_cow_self, ID *id_pointer);
+
IDNode *add_id_node(ID *id);
IDNode *find_id_node(ID *id);
TimeSourceNode *add_time_source();
@@ -276,6 +278,9 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
bool is_reference,
void *user_data);
+ void tag_previously_tagged_nodes();
+ void update_invalid_cow_pointers();
+
/* State which demotes currently built entities. */
Scene *scene_;
ViewLayer *view_layer_;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
index 13caba67713..00c78b8edce 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
@@ -169,7 +169,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible)
}
/* Speed optimization for animation lookups. */
if (object->pose != nullptr) {
- BKE_pose_channels_hash_make(object->pose);
+ BKE_pose_channels_hash_ensure(object->pose);
if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(object->pose);
}
@@ -318,7 +318,7 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object, bool is_object_visibl
/* Armature. */
build_armature(armature);
/* speed optimization for animation lookups */
- BKE_pose_channels_hash_make(object->pose);
+ BKE_pose_channels_hash_ensure(object->pose);
if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(object->pose);
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
index 197e14c1a21..17c2925b7f4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
@@ -30,14 +30,6 @@
namespace blender::deg {
-RootPChanMap::RootPChanMap()
-{
-}
-
-RootPChanMap::~RootPChanMap()
-{
-}
-
/* Debug contents of map */
void RootPChanMap::print_debug()
{
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
index 7a6ea38a0f0..0dd4062c353 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
@@ -29,10 +29,6 @@ namespace blender {
namespace deg {
struct RootPChanMap {
- /* Constructor and destructor - Create and free the internal map respectively. */
- RootPChanMap();
- ~RootPChanMap();
-
/* Debug contents of map. */
void print_debug();
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 3cc2ec02165..8a02228146a 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -635,11 +635,38 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll
ComponentKey duplicator_key(object != nullptr ? &object->id : nullptr, NodeType::DUPLI);
if (!group_done) {
build_idproperties(collection->id.properties);
+ OperationKey collection_geometry_key{
+ &collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE};
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
build_object(cob->ob);
+
+ /* The geometry of a collection depends on the positions of the elements in it. */
+ OperationKey object_transform_key{
+ &cob->ob->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL};
+ add_relation(object_transform_key, collection_geometry_key, "Collection Geometry");
+
+ /* Only create geometry relations to child objects, if they have a geometry component. */
+ OperationKey object_geometry_key{
+ &cob->ob->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL};
+ if (find_node(object_geometry_key) != nullptr) {
+ add_relation(object_geometry_key, collection_geometry_key, "Collection Geometry");
+ }
+
+ /* An instance is part of the geometry of the collection. */
+ if (cob->ob->type == OB_EMPTY) {
+ Collection *collection_instance = cob->ob->instance_collection;
+ if (collection_instance != nullptr) {
+ OperationKey collection_instance_key{
+ &collection_instance->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE};
+ add_relation(collection_instance_key, collection_geometry_key, "Collection Geometry");
+ }
+ }
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
build_collection(nullptr, nullptr, child->collection);
+ OperationKey child_collection_geometry_key{
+ &child->collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE};
+ add_relation(child_collection_geometry_key, collection_geometry_key, "Collection Geometry");
}
}
if (object != nullptr) {
@@ -2374,6 +2401,18 @@ void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket)
build_collection(nullptr, nullptr, collection);
}
}
+ else if (socket->type == SOCK_TEXTURE) {
+ Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value;
+ if (texture != nullptr) {
+ build_texture(texture);
+ }
+ }
+ else if (socket->type == SOCK_MATERIAL) {
+ Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value;
+ if (material != nullptr) {
+ build_material(material);
+ }
+ }
}
void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
index 4064058f231..54c51adec66 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
@@ -119,9 +119,7 @@ RNANodeQuery::RNANodeQuery(Depsgraph *depsgraph, DepsgraphBuilder *builder)
{
}
-RNANodeQuery::~RNANodeQuery()
-{
-}
+RNANodeQuery::~RNANodeQuery() = default;
Node *RNANodeQuery::find_node(const PointerRNA *ptr,
const PropertyRNA *prop,
diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc
index f7feeea9593..10bc7213061 100644
--- a/source/blender/depsgraph/intern/builder/pipeline.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline.cc
@@ -40,10 +40,6 @@ AbstractBuilderPipeline::AbstractBuilderPipeline(::Depsgraph *graph)
{
}
-AbstractBuilderPipeline::~AbstractBuilderPipeline()
-{
-}
-
void AbstractBuilderPipeline::build()
{
double start_time = 0.0;
@@ -98,7 +94,7 @@ void AbstractBuilderPipeline::build_step_finalize()
if (G.debug_value == 799) {
deg_graph_transitive_reduction(deg_graph_);
}
- /* Store pointers to commonly used valuated datablocks. */
+ /* Store pointers to commonly used evaluated datablocks. */
deg_graph_->scene_cow = (Scene *)deg_graph_->get_cow_id(&deg_graph_->scene->id);
/* Flush visibility layer and re-schedule nodes for update. */
deg_graph_build_finalize(bmain_, deg_graph_);
diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h
index dcd1bc6f26e..d0ccf352c21 100644
--- a/source/blender/depsgraph/intern/builder/pipeline.h
+++ b/source/blender/depsgraph/intern/builder/pipeline.h
@@ -50,7 +50,7 @@ class DepsgraphRelationBuilder;
class AbstractBuilderPipeline {
public:
AbstractBuilderPipeline(::Depsgraph *graph);
- virtual ~AbstractBuilderPipeline();
+ virtual ~AbstractBuilderPipeline() = default;
void build();
diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc
index 3d30e7e79b9..8e1ab23fae0 100644
--- a/source/blender/depsgraph/intern/depsgraph.cc
+++ b/source/blender/depsgraph/intern/depsgraph.cc
@@ -70,7 +70,8 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati
scene_cow(nullptr),
is_active(false),
is_evaluating(false),
- is_render_pipeline_depsgraph(false)
+ is_render_pipeline_depsgraph(false),
+ use_editors_update(false)
{
BLI_spin_init(&lock);
memset(id_type_updated, 0, sizeof(id_type_updated));
@@ -285,7 +286,9 @@ Depsgraph *DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEval
}
/* Replace the "owner" pointers (currently Main/Scene/ViewLayer) of this depsgraph.
- * Used during undo steps when we do want to re-use the old depsgraph data as much as possible. */
+ * Used for:
+ * - Undo steps when we do want to re-use the old depsgraph data as much as possible.
+ * - Rendering where we want to re-use objects between different view layers. */
void DEG_graph_replace_owners(struct Depsgraph *depsgraph,
Main *bmain,
Scene *scene,
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index df8c8215d2f..b87ce94709a 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -161,6 +161,9 @@ struct Depsgraph {
* does not need any bases. */
bool is_render_pipeline_depsgraph;
+ /* Notify editors about changes to IDs in this depsgraph. */
+ bool use_editors_update;
+
/* Cached list of colliders/effectors for collections and the scene
* created along with relations, for fast lookup during evaluation. */
Map<const ID *, ListBase *> *physics_relations[DEG_PHYSICS_RELATIONS_NUM];
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index 6717ba521f6..9e9191c5ab9 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -32,11 +32,13 @@
#include "PIL_time_utildefines.h"
#include "DNA_cachefile_types.h"
+#include "DNA_collection_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
+#include "BKE_collection.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -60,6 +62,7 @@
#include "intern/depsgraph_registry.h"
#include "intern/depsgraph_relation.h"
+#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
/* ****************** */
@@ -107,6 +110,34 @@ void DEG_add_object_relation(DepsNodeHandle *node_handle,
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
}
+bool DEG_object_has_geometry_component(Object *object)
+{
+ return deg::geometry_tag_to_component(&object->id) != deg::NodeType::UNDEFINED;
+}
+
+void DEG_add_collection_geometry_relation(DepsNodeHandle *node_handle,
+ Collection *collection,
+ const char *description)
+{
+ deg::OperationKey operation_key{
+ &collection->id, deg::NodeType::GEOMETRY, deg::OperationCode::GEOMETRY_EVAL_DONE};
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
+}
+
+void DEG_add_collection_geometry_customdata_mask(DepsNodeHandle *node_handle,
+ Collection *collection,
+ const CustomData_MeshMasks *masks)
+{
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
+ DEG_add_customdata_mask(node_handle, ob, masks);
+ if (ob->type == OB_EMPTY && ob->instance_collection != nullptr) {
+ DEG_add_collection_geometry_customdata_mask(node_handle, ob->instance_collection, masks);
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+}
+
void DEG_add_simulation_relation(DepsNodeHandle *node_handle,
Simulation *simulation,
const char *description)
diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc
index 57de62e1880..aab4c3ca0f6 100644
--- a/source/blender/depsgraph/intern/depsgraph_query.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query.cc
@@ -168,7 +168,7 @@ ViewLayer *DEG_get_evaluated_view_layer(const Depsgraph *graph)
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
Scene *scene_cow = DEG_get_evaluated_scene(graph);
if (scene_cow == nullptr) {
- return nullptr; /* Happens with new, not-yet-built/evaluated graphes. */
+ return nullptr; /* Happens with new, not-yet-built/evaluated graphs. */
}
/* Do name-based lookup. */
/* TODO(sergey): Can this be optimized? */
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index ed002321729..df1cf8cc771 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -219,6 +219,29 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
}
}
+ /* The curve component. */
+ if (data->geometry_component_id == 3) {
+ data->geometry_component_id++;
+
+ const CurveComponent *component = geometry_set->get_component_for_read<CurveComponent>();
+ if (component != nullptr) {
+ const Curve *curve = component->get_curve_for_render();
+
+ if (curve != nullptr) {
+ Object *temp_object = &data->temp_geometry_component_object;
+ *temp_object = *data->geometry_component_owner;
+ temp_object->type = OB_CURVE;
+ temp_object->data = (void *)curve;
+ /* Assign data_eval here too, because curve rendering code tries
+ * to use a mesh if it can find one in this pointer. */
+ temp_object->runtime.data_eval = (ID *)curve;
+ temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id;
+ iter->current = temp_object;
+ return true;
+ }
+ }
+ }
+
data->geometry_component_owner = nullptr;
return false;
}
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 2051ee3657a..204143d7cbd 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -594,6 +594,7 @@ NodeType geometry_tag_to_component(const ID *id)
case ID_HA:
case ID_PT:
case ID_VO:
+ case ID_GR:
return NodeType::GEOMETRY;
case ID_PA: /* Particles */
return NodeType::UNDEFINED;
@@ -816,11 +817,24 @@ void DEG_on_visible_update(Main *bmain, const bool do_time)
}
}
+void DEG_enable_editors_update(Depsgraph *depsgraph)
+{
+ deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph;
+ graph->use_editors_update = true;
+}
+
/* Check if something was changed in the database and inform
* editors about this. */
-void DEG_ids_check_recalc(
- Main *bmain, Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, bool time)
+void DEG_editors_update(Depsgraph *depsgraph, bool time)
{
+ deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph;
+ if (!graph->use_editors_update) {
+ return;
+ }
+
+ Scene *scene = DEG_get_input_scene(depsgraph);
+ ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ Main *bmain = DEG_get_bmain(depsgraph);
bool updated = time || DEG_id_type_any_updated(depsgraph);
DEGEditorUpdateContext update_ctx = {nullptr};
@@ -842,7 +856,7 @@ static void deg_graph_clear_id_recalc_flags(ID *id)
/* XXX And what about scene's master collection here? */
}
-void DEG_ids_clear_recalc(Main *UNUSED(bmain), Depsgraph *depsgraph)
+void DEG_ids_clear_recalc(Depsgraph *depsgraph, const bool backup)
{
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
/* TODO(sergey): Re-implement POST_UPDATE_HANDLER_WORKAROUND using entry_tags
@@ -852,6 +866,9 @@ void DEG_ids_clear_recalc(Main *UNUSED(bmain), Depsgraph *depsgraph)
}
/* Go over all ID nodes nodes, clearing tags. */
for (deg::IDNode *id_node : deg_graph->id_nodes) {
+ if (backup) {
+ id_node->id_cow_recalc_backup |= id_node->id_cow->recalc;
+ }
/* TODO: we clear original ID recalc flags here, but this may not work
* correctly when there are multiple depsgraph with others still using
* the recalc flag. */
@@ -863,3 +880,13 @@ void DEG_ids_clear_recalc(Main *UNUSED(bmain), Depsgraph *depsgraph)
}
memset(deg_graph->id_type_updated, 0, sizeof(deg_graph->id_type_updated));
}
+
+void DEG_ids_restore_recalc(Depsgraph *depsgraph)
+{
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
+
+ for (deg::IDNode *id_node : deg_graph->id_nodes) {
+ id_node->id_cow->recalc |= id_node->id_cow_recalc_backup;
+ id_node->id_cow_recalc_backup = 0;
+ }
+}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 620e86550cc..2107e075139 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -353,7 +353,8 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state)
return BLI_task_pool_create_no_threads(state);
}
- return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH);
+ /* TODO: Disable task isolation. */
+ return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
/**
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
index 2544bb1642c..0d367762b00 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
@@ -304,7 +304,8 @@ bool id_copy_inplace_no_main(const ID *id, ID *newid)
bool result = (BKE_id_copy_ex(nullptr,
(ID *)id_for_copy,
&newid,
- LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE) != nullptr);
+ (LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE |
+ LIB_ID_COPY_SET_COPIED_ON_WRITE)) != nullptr);
#ifdef NESTED_ID_NASTY_WORKAROUND
if (result) {
@@ -319,7 +320,6 @@ bool id_copy_inplace_no_main(const ID *id, ID *newid)
* is already allocated. */
bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene)
{
- const ID *id_for_copy = &scene->id;
if (G.debug & G_DEBUG_DEPSGRAPH_UUID) {
SEQ_relations_check_uuids_unique_and_report(scene);
@@ -327,13 +327,15 @@ bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene)
#ifdef NESTED_ID_NASTY_WORKAROUND
NestedIDHackTempStorage id_hack_storage;
- id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, &scene->id);
+ const ID *id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, &scene->id);
+#else
+ const ID *id_for_copy = &scene->id;
#endif
-
bool result = (BKE_id_copy_ex(nullptr,
id_for_copy,
(ID **)&new_scene,
- LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE) != nullptr);
+ (LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE |
+ LIB_ID_COPY_SET_COPIED_ON_WRITE)) != nullptr);
#ifdef NESTED_ID_NASTY_WORKAROUND
if (result) {
@@ -604,20 +606,12 @@ void update_lattice_edit_mode_pointers(const Depsgraph * /*depsgraph*/,
void update_mesh_edit_mode_pointers(const ID *id_orig, ID *id_cow)
{
- /* For meshes we need to update edit_mesh to make it to point
- * to the CoW version of object.
- *
- * This is kind of confusing, because actual bmesh is not owned by
- * the CoW object, so need to be accurate about using link from
- * edit_mesh to object. */
const Mesh *mesh_orig = (const Mesh *)id_orig;
Mesh *mesh_cow = (Mesh *)id_cow;
if (mesh_orig->edit_mesh == nullptr) {
return;
}
- mesh_cow->edit_mesh = (BMEditMesh *)MEM_dupallocN(mesh_orig->edit_mesh);
- mesh_cow->edit_mesh->mesh_eval_cage = nullptr;
- mesh_cow->edit_mesh->mesh_eval_final = nullptr;
+ mesh_cow->edit_mesh = mesh_orig->edit_mesh;
}
/* Edit data is stored and owned by original datablocks, copied ones
@@ -653,11 +647,17 @@ void update_list_orig_pointers(const ListBase *listbase_orig,
{
T *element_orig = reinterpret_cast<T *>(listbase_orig->first);
T *element_cow = reinterpret_cast<T *>(listbase->first);
- while (element_orig != nullptr) {
+
+ /* Both lists should have the same number of elements, so the check on
+ * `element_cow` is just to prevent a crash if this is not the case. */
+ while (element_orig != nullptr && element_cow != nullptr) {
element_cow->*orig_field = element_orig;
element_cow = element_cow->next;
element_orig = element_orig->next;
}
+
+ BLI_assert((element_orig == nullptr && element_cow == nullptr) ||
+ !"list of pointers of different sizes, unable to reliably set orig pointer");
}
void update_particle_system_orig_pointers(const Object *object_orig, Object *object_cow)
@@ -993,11 +993,6 @@ void discard_lattice_edit_mode_pointers(ID *id_cow)
void discard_mesh_edit_mode_pointers(ID *id_cow)
{
Mesh *mesh_cow = (Mesh *)id_cow;
- if (mesh_cow->edit_mesh == nullptr) {
- return;
- }
- BKE_editmesh_free_derivedmesh(mesh_cow->edit_mesh);
- MEM_freeN(mesh_cow->edit_mesh);
mesh_cow->edit_mesh = nullptr;
}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
index 7a0c1b5b693..7893e8c64c1 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
@@ -33,6 +33,7 @@ namespace blender::deg {
RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph)
: have_backup(false),
+ id_data({nullptr}),
animation_backup(depsgraph),
scene_backup(depsgraph),
sound_backup(depsgraph),
@@ -51,6 +52,8 @@ void RuntimeBackup::init_from_id(ID *id)
}
have_backup = true;
+ id_data.py_instance = id->py_instance;
+
animation_backup.init_from_id(id);
const ID_Type id_type = GS(id->name);
@@ -89,6 +92,8 @@ void RuntimeBackup::restore_to_id(ID *id)
return;
}
+ id->py_instance = id_data.py_instance;
+
animation_backup.restore_to_id(id);
const ID_Type id_type = GS(id->name);
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
index c6249c83daa..0629dbe62b4 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
@@ -58,6 +58,11 @@ class RuntimeBackup {
* copy-on-write mechanism. */
bool have_backup;
+ /* Struct members of the ID pointer. */
+ struct {
+ void *py_instance;
+ } id_data;
+
AnimationBackup animation_backup;
SceneBackup scene_backup;
SoundBackup sound_backup;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
index c1d2dd8b6cc..bd872d40825 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
@@ -75,19 +75,11 @@ void animated_property_store_cb(ID *id, FCurve *fcurve, void *data_v)
} // namespace
-AnimationValueBackup::AnimationValueBackup()
-{
-}
-
AnimationValueBackup::AnimationValueBackup(const string &rna_path, int array_index, float value)
: rna_path(rna_path), array_index(array_index), value(value)
{
}
-AnimationValueBackup::~AnimationValueBackup()
-{
-}
-
AnimationBackup::AnimationBackup(const Depsgraph *depsgraph)
{
meed_value_backup = !depsgraph->is_active;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
index 6b5d5eab75f..8f71457ae6f 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
@@ -34,9 +34,8 @@ struct Depsgraph;
class AnimationValueBackup {
public:
- AnimationValueBackup();
+ AnimationValueBackup() = default;
AnimationValueBackup(const string &rna_path, int array_index, float value);
- ~AnimationValueBackup();
AnimationValueBackup(const AnimationValueBackup &other) = default;
AnimationValueBackup(AnimationValueBackup &&other) noexcept = default;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
index 61299a2d49c..30ec9e948fd 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
@@ -55,7 +55,7 @@ void ObjectRuntimeBackup::init_from_object(Object *object)
/* Make a backup of base flags. */
base_flag = object->base_flag;
base_local_view_bits = object->base_local_view_bits;
- /* Backup tuntime data of all modifiers. */
+ /* Backup runtime data of all modifiers. */
backup_modifier_runtime_data(object);
/* Backup runtime data of all pose channels. */
backup_pose_channel_runtime_data(object);
@@ -98,7 +98,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object)
object->runtime = runtime;
object->runtime.data_orig = data_orig;
object->runtime.bb = bb;
- if (object->type == OB_MESH && data_eval != nullptr) {
+ if (ELEM(object->type, OB_MESH, OB_LATTICE) && data_eval != nullptr) {
if (object->id.recalc & ID_RECALC_GEOMETRY) {
/* If geometry is tagged for update it means, that part of
* evaluated mesh are not valid anymore. In this case we can not
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
index 35720140f97..d481e0c39a8 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
@@ -35,24 +35,28 @@ SequenceBackup::SequenceBackup(const Depsgraph * /*depsgraph*/)
void SequenceBackup::reset()
{
scene_sound = nullptr;
+ BLI_listbase_clear(&anims);
}
void SequenceBackup::init_from_sequence(Sequence *sequence)
{
scene_sound = sequence->scene_sound;
+ anims = sequence->anims;
sequence->scene_sound = nullptr;
+ BLI_listbase_clear(&sequence->anims);
}
void SequenceBackup::restore_to_sequence(Sequence *sequence)
{
sequence->scene_sound = scene_sound;
+ sequence->anims = anims;
reset();
}
bool SequenceBackup::isEmpty() const
{
- return (scene_sound == nullptr);
+ return (scene_sound == nullptr) && BLI_listbase_is_empty(&anims);
}
} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
index eb38dc3dc5b..3b3aa496496 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
@@ -23,6 +23,8 @@
#pragma once
+#include "BLI_listbase.h"
+
struct Sequence;
namespace blender {
@@ -43,6 +45,7 @@ class SequenceBackup {
bool isEmpty() const;
void *scene_sound;
+ ListBase anims;
};
} // namespace deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc
index 8e159a7ff08..688afe141e9 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_id.cc
@@ -90,6 +90,7 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata))
is_collection_fully_expanded = false;
has_base = false;
is_user_modified = false;
+ id_cow_recalc_backup = 0;
visible_components_mask = 0;
previously_visible_components_mask = 0;
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h
index e2d3b3fc36f..073469598dc 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.h
+++ b/source/blender/depsgraph/intern/node/deg_node_id.h
@@ -120,6 +120,9 @@ struct IDNode : public Node {
/* Accumulated flag from operation. Is initialized and used during updates flush. */
bool is_user_modified;
+ /* Accumulate recalc flags from multiple update passes. */
+ int id_cow_recalc_backup;
+
IDComponentsMask visible_components_mask;
IDComponentsMask previously_visible_components_mask;
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc
index 97aca6280be..7e57467f905 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc
@@ -213,10 +213,6 @@ OperationNode::OperationNode() : name_tag(-1), flag(0)
{
}
-OperationNode::~OperationNode()
-{
-}
-
string OperationNode::identifier() const
{
return string(operationCodeAsString(opcode)) + "(" + name + ")";
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h
index 40369de08f5..1d966cffd5d 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.h
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.h
@@ -231,7 +231,6 @@ enum OperationFlag {
/* Atomic Operation - Base type for all operations */
struct OperationNode : public Node {
OperationNode();
- ~OperationNode();
virtual string identifier() const override;
string full_identifier() const;
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 196b46953c4..e99bf1f5b0c 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -31,6 +31,7 @@ set(INC
../depsgraph
../editors/include
../editors/space_view3d
+ ../functions
../gpu
../imbuf
../makesdna
@@ -50,8 +51,17 @@ set(INC
set(SRC
intern/draw_cache.c
- intern/draw_cache_extract_mesh.c
- intern/draw_cache_impl_curve.c
+ intern/draw_cache_extract_mesh_extractors.c
+ intern/draw_cache_extract_mesh_render_data.c
+ intern/draw_cache_extract_mesh.cc
+ intern/mesh_extractors/extract_mesh_ibo_edituv.cc
+ intern/mesh_extractors/extract_mesh_ibo_fdots.cc
+ intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
+ intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
+ intern/mesh_extractors/extract_mesh_ibo_lines.cc
+ intern/mesh_extractors/extract_mesh_ibo_points.cc
+ intern/mesh_extractors/extract_mesh_ibo_tris.cc
+ intern/draw_cache_impl_curve.cc
intern/draw_cache_impl_displist.c
intern/draw_cache_impl_gpencil.c
intern/draw_cache_impl_hair.c
@@ -146,6 +156,7 @@ set(SRC
engines/overlay/overlay_image.c
engines/overlay/overlay_lattice.c
engines/overlay/overlay_metaball.c
+ engines/overlay/overlay_mode_transfer.c
engines/overlay/overlay_motion_path.c
engines/overlay/overlay_outline.c
engines/overlay/overlay_paint.c
@@ -161,6 +172,7 @@ set(SRC
intern/DRW_render.h
intern/draw_cache.h
intern/draw_cache_extract.h
+ intern/draw_cache_extract_mesh_private.h
intern/draw_cache_impl.h
intern/draw_cache_inline.h
intern/draw_color_management.h
@@ -321,6 +333,7 @@ data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC)
+data_to_c_simple(intern/shaders/common_hair_refine_comp.glsl SRC)
data_to_c_simple(intern/shaders/common_math_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_view_lib.glsl SRC)
@@ -478,6 +491,7 @@ add_dependencies(bf_draw bf_dna)
if(WITH_GTESTS)
if(WITH_OPENGL_DRAW_TESTS)
set(TEST_SRC
+ tests/draw_testing.cc
tests/shaders_test.cc
)
set(TEST_INC
diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index 2d5b93f4272..8a35ab2aeb9 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -118,8 +118,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
void DRW_draw_depth_loop(struct Depsgraph *depsgraph,
struct ARegion *region,
struct View3D *v3d,
- struct GPUViewport *viewport,
- bool use_opengl_context);
+ struct GPUViewport *viewport);
void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph,
struct ARegion *region,
struct View3D *v3d,
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 0cb2d55d1eb..7fe984b4397 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -121,7 +121,7 @@ void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata)
ViewLayer *view_layer = draw_ctx->view_layer;
/* Cryptomatte is only rendered for final image renders */
- if (!DRW_state_is_image_render()) {
+ if (!DRW_state_is_scene_render()) {
return;
}
const eViewLayerCryptomatteFlags active_layers = eevee_cryptomatte_active_layers(view_layer);
@@ -265,8 +265,7 @@ void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
Object *ob)
{
BLI_assert(ob->type == OB_HAIR);
- Hair *hair = ob->data;
- Material *material = hair->mat ? hair->mat[HAIR_MATERIAL_NR - 1] : NULL;
+ Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR);
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material);
}
@@ -291,8 +290,7 @@ void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
if (draw_as != PART_DRAW_PATH) {
continue;
}
- Mesh *mesh = ob->data;
- Material *material = part->omat - 1 < mesh->totcol ? NULL : mesh->mat[part->omat - 1];
+ Material *material = BKE_object_material_get_eval(ob, part->omat);
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material);
}
}
@@ -318,7 +316,7 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s
if (geom == NULL) {
continue;
}
- Material *material = BKE_object_material_get(ob, i + 1);
+ Material *material = BKE_object_material_get_eval(ob, i + 1);
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, false);
DRW_shgroup_call(grp, geom, ob);
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index f4f7acb8862..3a38edecec6 100644
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ b/source/blender/draw/engines/eevee/eevee_effects.c
@@ -171,7 +171,8 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
});
}
else {
- txl->filtered_radiance = NULL;
+ DRW_TEXTURE_FREE_SAFE(txl->filtered_radiance);
+ GPU_FRAMEBUFFER_FREE_SAFE(fbl->radiance_filtered_fb);
}
/**
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index ae726d7af9a..88fd823bc72 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -494,12 +494,7 @@ static void eevee_render_to_image(void *vedata,
/* Previous motion step. */
if (do_motion_blur_fx) {
- if (i > 0) {
- /* The previous step of this iteration N is exactly the next step of iteration N - 1.
- * So we just swap the resources to avoid too much re-evaluation. */
- EEVEE_motion_blur_swap_data(vedata);
- }
- else {
+ if (i == 0) {
EEVEE_motion_blur_step_set(ved, MB_PREV);
DRW_render_set_time(engine, depsgraph, floorf(time_prev), fractf(time_prev));
EEVEE_render_modules_init(vedata, engine, depsgraph);
@@ -561,6 +556,11 @@ static void eevee_render_to_image(void *vedata,
EEVEE_renderpasses_output_init(
sldata, vedata, g_data->render_sample_count_per_timestep * time_steps_tot);
+ if (scene->world) {
+ /* Update world in case of animated world material. */
+ eevee_id_world_update(vedata, scene->world);
+ }
+
EEVEE_temporal_sampling_create_view(vedata);
EEVEE_render_draw(vedata, engine, render_layer, rect);
@@ -570,6 +570,14 @@ static void eevee_render_to_image(void *vedata,
DRW_cache_restart();
}
}
+
+ if (do_motion_blur_fx) {
+ /* The previous step of next iteration N is exactly the next step of this iteration N - 1.
+ * So we just swap the resources to avoid too much re-evaluation.
+ * Note that this also clears the VBO references from the GPUBatches of deformed
+ * geometries. */
+ EEVEE_motion_blur_swap_data(vedata);
+ }
}
EEVEE_volumes_free_smoke_textures();
diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c
index cba86d058ea..e23a5a81169 100644
--- a/source/blender/draw/engines/eevee/eevee_lights.c
+++ b/source/blender/draw/engines/eevee/eevee_lights.c
@@ -96,7 +96,7 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli)
}
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
- power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* 1/(4*r²*Pi²) */
+ power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* `1/(4*(r^2)*(Pi^2))` */
/* for point lights (a.k.a radius == 0.0) */
// power = M_PI * M_PI * 0.78; /* XXX : Empirical, Fit cycles power */
@@ -106,7 +106,7 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli)
/* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that we
* cannot reproduce so we account for that by scaling the light power. This function is the
* result of a rough manual fitting. */
- power += 1.0f / (2.0f * M_PI); /* power *= 1 + r²/2 */
+ power += 1.0f / (2.0f * M_PI); /* `power *= 1 + (r^2)/2` */
}
return power;
}
@@ -257,7 +257,7 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
float power = max_fff(UNPACK3(evli->color)) * evli->volume;
if (power > 0.0f && evli->light_type != LA_SUN) {
/* The limit of the power attenuation function when the distance to the light goes to 0 is
- * 2 / r² where r is the light radius. We need to find the right radius that emits at most
+ * `2 / r^2` where r is the light radius. We need to find the right radius that emits at most
* the volume light upper bound. Inverting the function we get: */
float min_radius = 1.0f / sqrtf(0.5f * upper_bound / power);
/* Square it here to avoid a multiplication inside the shader. */
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index 042fa621117..d2e0c8308c5 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -623,6 +623,11 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata,
/* This GPUShader has already been used by another material.
* Add new shading group just after to avoid shader switching cost. */
grp = DRW_shgroup_create_sub(*grp_p);
+
+ /* Per material uniforms. */
+ if (use_ssrefract) {
+ DRW_shgroup_uniform_float_copy(grp, "refractionDepth", ma->refract_depth);
+ }
}
else {
*grp_p = grp = DRW_shgroup_create(sh, shading_pass);
@@ -718,7 +723,7 @@ BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot, bool holdou
if (holdout) {
return BKE_material_default_holdout();
}
- Material *ma = BKE_object_material_get(ob, slot + 1);
+ Material *ma = BKE_object_material_get_eval(ob, slot + 1);
if (ma == NULL) {
if (ob->type == OB_VOLUME) {
ma = BKE_material_default_volume();
@@ -765,12 +770,15 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata,
if (matcache.depth_grp) {
*matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp);
+ DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
}
if (matcache.shading_grp) {
*matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp);
+ DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
}
if (matcache.shadow_grp) {
*matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp);
+ DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
*cast_shadow = true;
}
@@ -958,22 +966,9 @@ void EEVEE_material_renderpasses_init(EEVEE_Data *vedata)
}
}
-static void material_renderpass_init(EEVEE_FramebufferList *fbl,
- GPUTexture **output_tx,
- const eGPUTextureFormat format,
- const bool do_clear)
+static void material_renderpass_init(GPUTexture **output_tx, const eGPUTextureFormat format)
{
DRW_texture_ensure_fullscreen_2d(output_tx, format, 0);
- /* Clear texture. */
- if (do_clear) {
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- /* TODO(fclem): replace by GPU_texture_clear once it is fast. */
- GPU_framebuffer_texture_attach(fbl->material_accum_fb, *output_tx, 0, 0);
- GPU_framebuffer_bind(fbl->material_accum_fb);
- GPU_framebuffer_clear_color(fbl->material_accum_fb, clear);
- GPU_framebuffer_bind(fbl->main_fb);
- GPU_framebuffer_texture_detach(fbl->material_accum_fb, *output_tx);
- }
}
void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples)
@@ -988,33 +983,32 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
/* Should be enough precision for many samples. */
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F;
- const bool do_clear = (effects->taa_current_sample == 1);
/* Create FrameBuffer. */
GPU_framebuffer_ensure_config(&fbl->material_accum_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_LEAVE});
if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
- material_renderpass_init(fbl, &txl->env_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->env_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
- material_renderpass_init(fbl, &txl->emit_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->emit_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
- material_renderpass_init(fbl, &txl->diff_color_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->diff_color_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
- material_renderpass_init(fbl, &txl->diff_light_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->diff_light_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
- material_renderpass_init(fbl, &txl->spec_color_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->spec_color_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
- material_renderpass_init(fbl, &txl->aov_surface_accum[aov_index], texture_format, do_clear);
+ material_renderpass_init(&txl->aov_surface_accum[aov_index], texture_format);
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
- material_renderpass_init(fbl, &txl->spec_light_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->spec_light_accum, texture_format);
if (effects->enabled_effects & EFFECT_SSR) {
EEVEE_reflection_output_init(sldata, vedata, tot_samples);
@@ -1022,7 +1016,8 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
}
}
-static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl,
+static void material_renderpass_accumulate(EEVEE_EffectsInfo *effects,
+ EEVEE_FramebufferList *fbl,
DRWPass *renderpass,
DRWPass *renderpass2,
EEVEE_PrivateData *pd,
@@ -1032,6 +1027,11 @@ static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl,
GPU_framebuffer_texture_attach(fbl->material_accum_fb, output_tx, 0, 0);
GPU_framebuffer_bind(fbl->material_accum_fb);
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->material_accum_fb, clear);
+ }
+
pd->renderpass_ubo = renderpass_option_ubo;
DRW_draw_pass(renderpass);
if (renderpass2) {
@@ -1053,15 +1053,21 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
DRWPass *material_accum_ps = psl->material_accum_ps;
DRWPass *background_accum_ps = psl->background_accum_ps;
if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
- material_renderpass_accumulate(
- fbl, background_accum_ps, NULL, pd, txl->env_accum, sldata->renderpass_ubo.environment);
+ material_renderpass_accumulate(effects,
+ fbl,
+ background_accum_ps,
+ NULL,
+ pd,
+ txl->env_accum,
+ sldata->renderpass_ubo.environment);
}
if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
material_renderpass_accumulate(
- fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit);
+ effects, fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
- material_renderpass_accumulate(fbl,
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
NULL,
pd,
@@ -1069,7 +1075,8 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
sldata->renderpass_ubo.diff_color);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
- material_renderpass_accumulate(fbl,
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
NULL,
pd,
@@ -1081,15 +1088,27 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
- material_renderpass_accumulate(fbl,
+ bool prev_ssr = sldata->common_data.ssr_toggle;
+ if (prev_ssr) {
+ /* We need to disable ssr here so output radiance is not directed to the ssr buffer. */
+ sldata->common_data.ssr_toggle = false;
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
+ }
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
NULL,
pd,
txl->spec_color_accum,
sldata->renderpass_ubo.spec_color);
+ if (prev_ssr) {
+ sldata->common_data.ssr_toggle = prev_ssr;
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
+ }
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
- material_renderpass_accumulate(fbl,
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
NULL,
pd,
@@ -1102,7 +1121,8 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
}
if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
- material_renderpass_accumulate(fbl,
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
background_accum_ps,
pd,
diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c
index 2b448695528..d4490d6fd4c 100644
--- a/source/blender/draw/engines/eevee/eevee_mist.c
+++ b/source/blender/draw/engines/eevee/eevee_mist.c
@@ -40,12 +40,9 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PassList *psl = vedata->psl;
- EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
Scene *scene = draw_ctx->scene;
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
/* Create FrameBuffer. */
/* Should be enough precision for many samples. */
DRW_texture_ensure_fullscreen_2d(&txl->mist_accum, GPU_R32F, 0);
@@ -53,12 +50,6 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
GPU_framebuffer_ensure_config(&fbl->mist_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->mist_accum)});
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->mist_accum_fb);
- GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
- }
-
/* Mist settings. */
if (scene && scene->world) {
g_data->mist_start = scene->world->miststa;
@@ -103,9 +94,17 @@ void EEVEE_mist_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Dat
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->mist_accum_fb != NULL) {
GPU_framebuffer_bind(fbl->mist_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->mist_accum_ps);
/* Restore */
diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c
index 3f198063c47..4c2024a6f65 100644
--- a/source/blender/draw/engines/eevee/eevee_occlusion.c
+++ b/source/blender/draw/engines/eevee/eevee_occlusion.c
@@ -61,7 +61,7 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
common_data->ao_dist = scene_eval->eevee.gtao_distance;
- common_data->ao_factor = scene_eval->eevee.gtao_factor;
+ common_data->ao_factor = max_ff(1e-4f, scene_eval->eevee.gtao_factor);
common_data->ao_quality = scene_eval->eevee.gtao_quality;
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) {
@@ -120,7 +120,6 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* Should be enough precision for many samples. */
DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0);
@@ -128,12 +127,6 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
GPU_framebuffer_ensure_config(&fbl->ao_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)});
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->ao_accum_fb);
- GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
- }
-
/* Accumulation pass */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD;
DRW_PASS_CREATE(psl->ao_accum_ps, state);
@@ -246,6 +239,7 @@ void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->ao_accum_fb != NULL) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
@@ -255,6 +249,13 @@ void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *
EEVEE_occlusion_compute(sldata, vedata);
GPU_framebuffer_bind(fbl->ao_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->ao_accum_ps);
/* Restore */
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index 5739024993e..5ada53ab98c 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -79,7 +79,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata)
* type the rest of the bits are used for the name hash. */
int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov)
{
- int hash = BLI_hash_string(aov->name);
+ int hash = BLI_hash_string(aov->name) << 1;
SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK);
return hash;
}
diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
index 54f23073bd0..7af0f60748b 100644
--- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
+++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
@@ -234,10 +234,6 @@ void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* Create FrameBuffer. */
const eGPUTextureFormat texture_format = (tot_samples > 256) ? GPU_RGBA32F : GPU_RGBA16F;
@@ -245,12 +241,6 @@ void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
GPU_framebuffer_ensure_config(&fbl->ssr_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ssr_accum)});
-
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->ssr_accum_fb);
- GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear);
- }
}
void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
@@ -258,9 +248,17 @@ void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEV
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (stl->g_data->valid_double_buffer) {
GPU_framebuffer_bind(fbl->ssr_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->ssr_resolve);
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
index 51fd1cad41e..6a98c3316f3 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows.c
@@ -361,12 +361,8 @@ void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
/* Create FrameBuffer. */
const eGPUTextureFormat texture_format = GPU_R32F;
DRW_texture_ensure_fullscreen_2d(&txl->shadow_accum, texture_format, 0);
@@ -374,12 +370,6 @@ void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata,
GPU_framebuffer_ensure_config(&fbl->shadow_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->shadow_accum)});
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->shadow_accum_fb);
- GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear);
- }
-
/* Create Pass and shgroup. */
DRW_PASS_CREATE(psl->shadow_accum_pass,
DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ADD_FULL);
@@ -404,9 +394,17 @@ void EEVEE_shadow_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_D
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->shadow_accum_fb != NULL) {
GPU_framebuffer_bind(fbl->shadow_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->shadow_accum_pass);
/* Restore */
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index aee260a542e..eed36221fcb 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -332,7 +332,7 @@ static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWS
bool multiple_transforms = true;
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, gpu_grid->name);
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name);
DRWVolumeGrid *drw_grid = (volume_grid) ?
DRW_volume_batch_cache_get_grid(volume, volume_grid) :
NULL;
@@ -385,7 +385,7 @@ static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWS
/* Bind volume grid textures. */
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, gpu_grid->name);
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name);
DRWVolumeGrid *drw_grid = (volume_grid) ?
DRW_volume_batch_cache_get_grid(volume, volume_grid) :
NULL;
@@ -501,7 +501,7 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata,
Scene *scene,
Object *ob)
{
- Material *ma = BKE_object_material_get(ob, 1);
+ Material *ma = BKE_object_material_get_eval(ob, 1);
if (ma == NULL) {
if (ob->type == OB_VOLUME) {
@@ -800,8 +800,6 @@ void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = stl->effects;
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
/* Create FrameBuffer. */
/* Should be enough precision for many samples. */
@@ -814,12 +812,6 @@ void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_accum),
GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance_accum)});
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->volumetric_accum_fb);
- GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear);
- }
-
/* Create Pass and shgroup. */
DRW_PASS_CREATE(psl->volumetric_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
DRWShadingGroup *grp = NULL;
@@ -843,10 +835,18 @@ void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->volumetric_accum_fb != NULL) {
/* Accum pass */
GPU_framebuffer_bind(fbl->volumetric_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->volumetric_accum_ps);
/* Restore */
diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
index 69b9ecaf77d..cdc453eed40 100644
--- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
@@ -210,14 +210,14 @@ void occlusion_eval(OcclusionData data,
bool early_out = (inverted != 0.0) ? (max_v4(abs(data.horizons)) == 0.0) :
(min_v4(abs(data.horizons)) == M_PI);
if (early_out) {
- visibility = dot(N, Ng) * 0.5 + 0.5;
+ visibility = saturate(dot(N, Ng) * 0.5 + 0.5);
visibility = min(visibility, data.custom_occlusion);
if ((int(aoSettings) & USE_BENT_NORMAL) == 0) {
bent_normal = N;
}
else {
- bent_normal = normalize(N + Ng);
+ bent_normal = safe_normalize(N + Ng);
}
return;
}
@@ -283,7 +283,9 @@ void occlusion_eval(OcclusionData data,
bent_normal = N;
}
else {
- bent_normal = normalize(mix(bent_normal, N, sqr(sqr(sqr(visibility)))));
+ /* Note: using pow(visibility, 6.0) produces NaN (see T87369). */
+ float tmp = saturate(pow6(visibility));
+ bent_normal = normalize(mix(bent_normal, N, tmp));
}
}
@@ -386,7 +388,8 @@ float specular_occlusion(
float specular_solid_angle = spherical_cap_intersection(M_PI_2, spec_angle, cone_nor_dist);
float specular_occlusion = isect_solid_angle / specular_solid_angle;
/* Mix because it is unstable in unoccluded areas. */
- visibility = mix(specular_occlusion, 1.0, pow(visibility, 8.0));
+ float tmp = saturate(pow8(visibility));
+ visibility = mix(specular_occlusion, 1.0, tmp);
/* Scale by user factor */
visibility = pow(saturate(visibility), aoFactor);
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
index c8eaa06094e..05496ad4ab0 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -164,8 +164,8 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N)
vec2 N_new;
if (valid1 && valid2) {
/* If both are possible, do the expensive reflection-based check. */
- vec2 N1 = vec2(sqrt(1.0 - N1_z2), sqrt(N1_z2));
- vec2 N2 = vec2(sqrt(1.0 - N2_z2), sqrt(N2_z2));
+ vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2));
+ vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2));
float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz;
float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz;
@@ -181,7 +181,7 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N)
}
else if (valid1 || valid2) {
float Nz2 = valid1 ? N1_z2 : N2_z2;
- N_new = vec2(sqrt(1.0 - Nz2), sqrt(Nz2));
+ N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2));
}
else {
return Ng;
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
index b554fd113df..94dd1a439db 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
@@ -51,53 +51,72 @@
closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, sub_data, out_##t2##_2); \
closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, sub_data, out_##t3##_3);
+#ifndef DEPTH_SHADER
/* Inputs are inout so that callers can get the final inputs used for evaluation. */
-#define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
- void closure_##name##_eval(ClosureInputCommon in_common, \
- inout ClosureInput##t0 in_##t0##_0, \
- inout ClosureInput##t1 in_##t1##_1, \
- inout ClosureInput##t2 in_##t2##_2, \
- inout ClosureInput##t3 in_##t3##_3, \
- out ClosureOutput##t0 out_##t0##_0, \
- out ClosureOutput##t1 out_##t1##_1, \
- out ClosureOutput##t2 out_##t2##_2, \
- out ClosureOutput##t3 out_##t3##_3) \
- { \
- CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
+# define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
+ void closure_##name##_eval(ClosureInputCommon in_common, \
+ inout ClosureInput##t0 in_##t0##_0, \
+ inout ClosureInput##t1 in_##t1##_1, \
+ inout ClosureInput##t2 in_##t2##_2, \
+ inout ClosureInput##t3 in_##t3##_3, \
+ out ClosureOutput##t0 out_##t0##_0, \
+ out ClosureOutput##t1 out_##t1##_1, \
+ out ClosureOutput##t2 out_##t2##_2, \
+ out ClosureOutput##t3 out_##t3##_3) \
+ { \
+ CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
\
- /* Starts at 1 because 0 is world cubemap. */ \
- for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; \
- i++) { \
- ClosureCubemapData cube = closure_cubemap_eval_init(i, cl_common); \
- if (cube.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \
+ /* Starts at 1 because 0 is world cubemap. */ \
+ for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; \
+ i++) { \
+ ClosureCubemapData cube = closure_cubemap_eval_init(i, cl_common); \
+ if (cube.attenuation > 1e-8) { \
+ CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \
+ } \
} \
- } \
\
- /* Starts at 1 because 0 is world irradiance. */ \
- for (int i = 1; cl_common.diffuse_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; i++) { \
- ClosureGridData grid = closure_grid_eval_init(i, cl_common); \
- if (grid.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \
+ /* Starts at 1 because 0 is world irradiance. */ \
+ for (int i = 1; cl_common.diffuse_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; \
+ i++) { \
+ ClosureGridData grid = closure_grid_eval_init(i, cl_common); \
+ if (grid.attenuation > 1e-8) { \
+ CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \
+ } \
} \
- } \
\
- CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \
+ CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \
\
- ClosurePlanarData planar = closure_planar_eval_init(cl_common); \
- if (planar.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \
- } \
+ ClosurePlanarData planar = closure_planar_eval_init(cl_common); \
+ if (planar.attenuation > 1e-8) { \
+ CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \
+ } \
\
- for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \
- ClosureLightData light = closure_light_eval_init(cl_common, i); \
- if (light.vis > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \
+ for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \
+ ClosureLightData light = closure_light_eval_init(cl_common, i); \
+ if (light.vis > 1e-8) { \
+ CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \
+ } \
} \
- } \
\
- CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \
- }
+ CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \
+ }
+
+#else
+/* Inputs are inout so that callers can get the final inputs used for evaluation. */
+# define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
+ void closure_##name##_eval(ClosureInputCommon in_common, \
+ inout ClosureInput##t0 in_##t0##_0, \
+ inout ClosureInput##t1 in_##t1##_1, \
+ inout ClosureInput##t2 in_##t2##_2, \
+ inout ClosureInput##t3 in_##t3##_3, \
+ out ClosureOutput##t0 out_##t0##_0, \
+ out ClosureOutput##t1 out_##t1##_1, \
+ out ClosureOutput##t2 out_##t2##_2, \
+ out ClosureOutput##t3 out_##t3##_3) \
+ { \
+ CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
+ }
+#endif
#define CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) \
closure_##name##_eval(in_common, \
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
index faac216480b..dac53719149 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
@@ -136,7 +136,7 @@ const float layer_offset_fg = 0.5 + 1.0;
/* Extra offset for convolution layers to avoid light leaking from background. */
const float layer_offset = 0.5 + 0.5;
-#define DOF_MAX_SLIGHT_FOCUS_RADIUS 5
+#define DOF_MAX_SLIGHT_FOCUS_RADIUS 16
float dof_layer_weight(float coc, const bool is_foreground)
{
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
index cd69a0e2ab1..32841b7749c 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
@@ -40,7 +40,7 @@ void dof_slight_focus_gather(float radius, out vec4 out_color, out float out_wei
DofGatherData fg_accum = GATHER_DATA_INIT;
DofGatherData bg_accum = GATHER_DATA_INIT;
- int i_radius = clamp(int(radius), 0, int(layer_threshold));
+ int i_radius = clamp(int(radius + 0.5), 0, int(layer_threshold));
const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY;
ivec2 texel = ivec2(gl_FragCoord.xy);
diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
index 737ef7dc509..356ed102928 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
@@ -183,16 +183,70 @@ vec3 light_translucent(LightData ld, vec3 P, vec3 N, vec4 l_vector, vec2 rand, f
#undef scube
#undef scsmd
+/* Similar to https://atyuwen.github.io/posts/normal-reconstruction/.
+ * This samples the depth buffer 4 time for each direction to get the most correct
+ * implicit normal reconstruction out of the depth buffer. */
+vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float depth_center)
+{
+ vec2 uv1 = uvs - ofs * 2.0;
+ vec2 uv2 = uvs - ofs;
+ vec2 uv3 = uvs + ofs;
+ vec2 uv4 = uvs + ofs * 2.0;
+ vec4 H;
+ H.x = textureLod(depthBuffer, uv1, 0.0).r;
+ H.y = textureLod(depthBuffer, uv2, 0.0).r;
+ H.z = textureLod(depthBuffer, uv3, 0.0).r;
+ H.w = textureLod(depthBuffer, uv4, 0.0).r;
+ /* Fix issue with depth precision. Take even larger diff. */
+ vec4 diff = abs(vec4(depth_center, H.yzw) - H.x);
+ if (max_v4(diff) < 2.4e-7 && all(lessThan(diff.xyz, diff.www))) {
+ return 0.25 * (get_view_space_from_depth(uv3, H.w) - get_view_space_from_depth(uv1, H.x));
+ }
+ /* Simplified (H.xw + 2.0 * (H.yz - H.xw)) - depth_center */
+ vec2 deltas = abs((2.0 * H.yz - H.xw) - depth_center);
+ if (deltas.x < deltas.y) {
+ return vP - get_view_space_from_depth(uv2, H.y);
+ }
+ else {
+ return get_view_space_from_depth(uv3, H.z) - vP;
+ }
+}
+
+/* TODO(fclem) port to a common place for other effects to use. */
+bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg)
+{
+ vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y)));
+ float depth_center = textureLod(depthBuffer, uvs, 0.0).r;
+
+ vP = get_view_space_from_depth(uvs, depth_center);
+
+ vec3 dPdx = view_position_derivative_from_depth(uvs, texel_size * vec2(1, 0), vP, depth_center);
+ vec3 dPdy = view_position_derivative_from_depth(uvs, texel_size * vec2(0, 1), vP, depth_center);
+
+ vNg = safe_normalize(cross(dPdx, dPdy));
+
+ /* Background case. */
+ if (depth_center == 1.0) {
+ return false;
+ }
+
+ return true;
+}
+
void main(void)
{
vec2 uvs = uvcoordsvar.xy;
float sss_scale = texture(sssRadius, uvs).r;
- vec3 P = get_world_space_from_depth(uvs, texture(depthBuffer, uvs).r);
- vec3 N = normalize(cross(dFdx(P), dFdy(P)));
vec3 rand = texelfetch_noise_tex(gl_FragCoord.xy).zwy;
rand.xy *= fast_sqrt(rand.z);
+ vec3 vP, vNg;
+ reconstruct_view_position_and_normal_from_depth(uvs, vP, vNg);
+
+ vec3 P = point_view_to_world(vP);
+ vec3 Ng = normal_view_to_world(vNg);
+
vec3 accum = vec3(0.0);
for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
LightData ld = lights_data[i];
@@ -211,7 +265,7 @@ void main(void)
continue;
}
- accum += att * ld.l_color * light_translucent(ld, P, -N, l_vector, rand.xy, sss_scale);
+ accum += att * ld.l_color * light_translucent(ld, P, -Ng, l_vector, rand.xy, sss_scale);
}
FragColor = vec4(accum, 1.0);
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index ee51b751187..af8b029a08e 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -60,7 +60,8 @@ GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob)
/* Check if any material with holdout flag enabled. */
tgp_ob->do_mat_holdout = false;
- for (int i = 0; i < ob->totcol; i++) {
+ const int tot_materials = BKE_object_material_count_eval(ob);
+ for (int i = 0; i < tot_materials; i++) {
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1);
if (((gp_style != NULL) && (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT)) ||
((gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT))) {
@@ -273,7 +274,13 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
const bool override_vertcol = (pd->v3d_color_type != -1);
const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) ||
GPENCIL_VERTEX_MODE(gpd) || pd->is_render;
- bool is_masked = (gpl->flag & GP_LAYER_USE_MASK) && !BLI_listbase_is_empty(&gpl->mask_layers);
+ const bool is_viewlayer_render = pd->is_render && (gpl->viewlayername[0] != '\0') &&
+ STREQ(pd->view_layer->name, gpl->viewlayername);
+ const bool disable_masks_render = is_viewlayer_render &&
+ (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER);
+ bool is_masked = disable_masks_render ? false :
+ (gpl->flag & GP_LAYER_USE_MASK) &&
+ !BLI_listbase_is_empty(&gpl->mask_layers);
float vert_col_opacity = (override_vertcol) ?
(is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
index 28c322b5e08..e3e84dd4c8c 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
@@ -105,10 +105,12 @@ static void gpencil_shade_color(float color[3])
}
/* Apply all overrides from the solid viewport mode to the GPencil material. */
-static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_PrivateData *pd,
- Object *ob,
- int color_type,
- MaterialGPencilStyle *gp_style)
+static MaterialGPencilStyle *gpencil_viewport_material_overrides(
+ GPENCIL_PrivateData *pd,
+ Object *ob,
+ int color_type,
+ MaterialGPencilStyle *gp_style,
+ const eV3DShadingLightingMode lighting_mode)
{
static MaterialGPencilStyle gp_style_tmp;
@@ -148,7 +150,9 @@ static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_Private
copy_v3_v3(gp_style->fill_rgba, pd->v3d_single_color);
gp_style->fill_rgba[3] = 1.0f;
copy_v4_v4(gp_style->stroke_rgba, gp_style->fill_rgba);
- gpencil_shade_color(gp_style->stroke_rgba);
+ if (lighting_mode != V3D_LIGHTING_FLAT) {
+ gpencil_shade_color(gp_style->fill_rgba);
+ }
break;
case V3D_SHADING_OBJECT_COLOR:
gp_style = &gp_style_tmp;
@@ -156,7 +160,9 @@ static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_Private
gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
copy_v4_v4(gp_style->fill_rgba, ob->color);
copy_v4_v4(gp_style->stroke_rgba, ob->color);
- gpencil_shade_color(gp_style->stroke_rgba);
+ if (lighting_mode != V3D_LIGHTING_FLAT) {
+ gpencil_shade_color(gp_style->fill_rgba);
+ }
break;
case V3D_SHADING_VERTEX_COLOR:
gp_style = &gp_style_tmp;
@@ -180,7 +186,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
{
GPENCIL_MaterialPool *matpool = pd->last_material_pool;
- int mat_len = max_ii(1, ob->totcol);
+ int mat_len = max_ii(1, BKE_object_material_count_eval(ob));
bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GP_MATERIAL_BUFFER_LEN);
@@ -198,6 +204,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
int color_type = (pd->v3d_color_type != -1 && GPENCIL_VERTEX_MODE(gpd)) ?
V3D_SHADING_VERTEX_COLOR :
pd->v3d_color_type;
+ const eV3DShadingLightingMode lighting_mode = (pd->v3d != NULL) ? pd->v3d->shading.light :
+ V3D_LIGHTING_STUDIO;
GPENCIL_MaterialPool *pool = matpool;
for (int i = 0; i < mat_len; i++) {
@@ -245,7 +253,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
mat_data->flag |= GP_FILL_HOLDOUT;
}
- gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style);
+ gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style, lighting_mode);
/* Dots or Squares rotation. */
mat_data->alignment_rot_cos = cosf(gp_style->alignment_rotation);
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 8bb336ebc96..32884eb9e3f 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -819,7 +819,10 @@ static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL
}
GPENCIL_tLayer *mask_layer = gpencil_layer_cache_get(ob, i);
- BLI_assert(mask_layer);
+ /* When filtering by viewlayer, the mask could be null and must be ignored. */
+ if (mask_layer == NULL) {
+ continue;
+ }
DRW_draw_pass(mask_layer->geom_ps);
}
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index 5ceb909bc88..34fe29055d6 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -297,7 +297,9 @@ typedef struct GPENCIL_PrivateData {
int v3d_color_type;
/* Current frame */
int cfra;
- /* If we are rendering for final render (F12). */
+ /* If we are rendering for final render (F12).
+ * NOTE: set to false for viewport and opengl rendering (including VSE scene rendering), but set
+ * to true when rendering in `OB_RENDER` shading mode (viewport or opengl rendering) */
bool is_render;
/* If we are in viewport display (used for VFX). */
bool is_viewport;
diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
index 0b66141e51a..21d55357a2a 100644
--- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
+++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
@@ -424,22 +424,26 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx
GPUShader *sh = GPENCIL_shader_fx_glow_get();
- float ref_col[3];
+ float ref_col[4];
if (fx->mode == eShaderFxGlowMode_Luminance) {
+ /* Only pass in the first value for luminance. */
ref_col[0] = fx->threshold;
ref_col[1] = -1.0f;
ref_col[2] = -1.0f;
+ ref_col[3] = -1.0f;
}
else {
+ /* First three values are the RGB for the selected color, last value the threshold. */
copy_v3_v3(ref_col, fx->select_color);
+ ref_col[3] = fx->threshold;
}
DRWState state = DRW_STATE_WRITE_COLOR;
grp = gpencil_vfx_pass_create("Fx Glow H", state, iter, sh);
DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){fx->blur[0] * c, fx->blur[0] * s});
DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0])));
- DRW_shgroup_uniform_vec3_copy(grp, "threshold", ref_col);
+ DRW_shgroup_uniform_vec4_copy(grp, "threshold", ref_col);
DRW_shgroup_uniform_vec4_copy(grp, "glowColor", fx->glow_color);
DRW_shgroup_uniform_bool_copy(grp, "glowUnder", use_glow_under);
DRW_shgroup_uniform_bool_copy(grp, "firstPass", true);
@@ -473,7 +477,7 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx
grp = gpencil_vfx_pass_create("Fx Glow V", state, iter, sh);
DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-fx->blur[1] * s, fx->blur[1] * c});
DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0])));
- DRW_shgroup_uniform_vec3_copy(grp, "threshold", (float[3]){-1.0f, -1.0f, -1.0f});
+ DRW_shgroup_uniform_vec4_copy(grp, "threshold", (float[4]){-1.0f, -1.0f, -1.0f, -1.0});
DRW_shgroup_uniform_vec4_copy(grp, "glowColor", (float[4]){1.0f, 1.0f, 1.0f, fx->glow_color[3]});
DRW_shgroup_uniform_bool_copy(grp, "firstPass", false);
DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->blend_mode);
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
index 7412959a30b..ac48b94fea9 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
@@ -442,6 +442,10 @@ void stroke_vertex()
if (is_dot) {
# ifdef GP_MATERIAL_BUFFER_LEN
int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT;
+ /* For one point strokes use object aligment. */
+ if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) {
+ alignement = GP_STROKE_ALIGNMENT_OBJECT;
+ }
# endif
vec2 x_axis;
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
index bb905f8694b..269ed49c4d0 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
@@ -145,7 +145,7 @@ void main()
uniform vec4 glowColor;
uniform vec2 offset;
uniform int sampCount;
-uniform vec3 threshold;
+uniform vec4 threshold;
uniform bool firstPass;
uniform bool glowUnder;
uniform int blendMode;
@@ -168,7 +168,7 @@ void main()
vec3 rev = texture(revealBuf, uv).rgb;
if (threshold.x > -1.0) {
if (threshold.y > -1.0) {
- if (any(greaterThan(abs(col - threshold), vec3(0.05)))) {
+ if (any(greaterThan(abs(col - vec3(threshold)), vec3(threshold.w)))) {
weight = 0.0;
}
}
diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c
index d75f887ce2b..2522a445d8e 100644
--- a/source/blender/draw/engines/image/image_engine.c
+++ b/source/blender/draw/engines/image/image_engine.c
@@ -107,7 +107,7 @@ static void space_image_gpu_texture_get(Image *image,
const DRWContextState *draw_ctx = DRW_context_state_get();
SpaceImage *sima = (SpaceImage *)draw_ctx->space_data;
if (BKE_image_is_multilayer(image)) {
- /* update multiindex and pass for the current eye */
+ /* Update multi-index and pass for the current eye. */
BKE_image_multilayer_index(image->rr, &sima->iuser);
}
BKE_image_multiview_index(image, &sima->iuser);
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c
index 7042d095b56..2f9ececc198 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.c
@@ -1031,7 +1031,7 @@ static void pchan_draw_data_init(bPoseChannel *pchan)
static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *pchan)
{
float ebmat[4][4];
- float length;
+ float bone_scale[3];
float(*bone_mat)[4];
float(*disp_mat)[4];
float(*disp_tail_mat)[4];
@@ -1040,23 +1040,23 @@ static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *
* and not be tight to the draw pass creation.
* This would refresh armature without invalidating the draw cache */
if (pchan) {
- length = pchan->bone->length;
bone_mat = pchan->pose_mat;
disp_mat = pchan->disp_mat;
disp_tail_mat = pchan->disp_tail_mat;
+ copy_v3_fl(bone_scale, pchan->bone->length);
}
else {
eBone->length = len_v3v3(eBone->tail, eBone->head);
ED_armature_ebone_to_mat4(eBone, ebmat);
- length = eBone->length;
+ copy_v3_fl(bone_scale, eBone->length);
bone_mat = ebmat;
disp_mat = eBone->disp_mat;
disp_tail_mat = eBone->disp_tail_mat;
}
copy_m4_m4(disp_mat, bone_mat);
- rescale_m4(disp_mat, (float[3]){length, length, length});
+ rescale_m4(disp_mat, bone_scale);
copy_m4_m4(disp_tail_mat, disp_mat);
translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f);
}
@@ -1255,19 +1255,27 @@ static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pc
static void draw_bone_update_disp_matrix_custom(bPoseChannel *pchan)
{
- float length;
+ float bone_scale[3];
float(*bone_mat)[4];
float(*disp_mat)[4];
float(*disp_tail_mat)[4];
+ float rot_mat[3][3];
/* See TODO above */
- length = PCHAN_CUSTOM_DRAW_SIZE(pchan);
+ mul_v3_v3fl(bone_scale, pchan->custom_scale_xyz, PCHAN_CUSTOM_BONE_LENGTH(pchan));
bone_mat = pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat;
disp_mat = pchan->disp_mat;
disp_tail_mat = pchan->disp_tail_mat;
+ eulO_to_mat3(rot_mat, pchan->custom_rotation_euler, ROT_MODE_XYZ);
+
copy_m4_m4(disp_mat, bone_mat);
- rescale_m4(disp_mat, (float[3]){length, length, length});
+ translate_m4(disp_mat,
+ pchan->custom_translation[0],
+ pchan->custom_translation[1],
+ pchan->custom_translation[2]);
+ mul_m4_m4m3(disp_mat, disp_mat, rot_mat);
+ rescale_m4(disp_mat, bone_scale);
copy_m4_m4(disp_tail_mat, disp_mat);
translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f);
}
@@ -1293,11 +1301,15 @@ static void draw_axes(ArmatureDrawContext *ctx,
float length = pchan->bone->length;
copy_m4_m4(axis_mat, pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat);
rescale_m4(axis_mat, (float[3]){length, length, length});
+ translate_m4(axis_mat, 0.0, arm->axes_position - 1.0, 0.0);
drw_shgroup_bone_axes(ctx, axis_mat, final_col);
}
else {
- drw_shgroup_bone_axes(ctx, BONE_VAR(eBone, pchan, disp_mat), final_col);
+ float disp_mat[4][4];
+ copy_m4_m4(disp_mat, BONE_VAR(eBone, pchan, disp_mat));
+ translate_m4(disp_mat, 0.0, arm->axes_position - 1.0, 0.0);
+ drw_shgroup_bone_axes(ctx, disp_mat, final_col);
}
}
@@ -2051,9 +2063,8 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
set_pchan_colorset(ctx, ob, pchan);
}
- int boneflag = bone->flag;
/* catch exception for bone with hidden parent */
- boneflag = bone->flag;
+ int boneflag = bone->flag;
if ((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) {
boneflag &= ~BONE_CONNECTED;
}
diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
index 40a9dbe01c0..7639911286f 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
@@ -149,7 +149,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
DRWState state_common = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL |
DRW_STATE_BLEND_ALPHA;
/* Faces */
- /* Cage geom needs to be offsetted to avoid Z-fighting. */
+ /* Cage geom needs an offset applied to avoid Z-fighting. */
for (int j = 0; j < 2; j++) {
DRWPass **edit_face_ps = (j == 0) ? &psl->edit_mesh_faces_ps[i] :
&psl->edit_mesh_faces_cage_ps[i];
@@ -197,7 +197,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
grp = pd->edit_mesh_skin_roots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
}
- /* Facedots */
+ /* Face-dots */
if (select_face && show_face_dots) {
sh = OVERLAY_shader_edit_mesh_facedot();
grp = pd->edit_mesh_facedots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]);
diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c
index e9736402ae7..81b07b49784 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.c
@@ -207,6 +207,7 @@ static void OVERLAY_cache_init(void *vedata)
OVERLAY_armature_cache_init(vedata);
OVERLAY_background_cache_init(vedata);
OVERLAY_fade_cache_init(vedata);
+ OVERLAY_mode_transfer_cache_init(vedata);
OVERLAY_extra_cache_init(vedata);
OVERLAY_facing_cache_init(vedata);
OVERLAY_gpencil_cache_init(vedata);
@@ -285,10 +286,6 @@ static bool overlay_should_fade_object(Object *ob, Object *active_object)
return false;
}
- if (ob->base_flag & BASE_FROM_DUPLI) {
- return false;
- }
-
return true;
}
@@ -327,6 +324,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
!is_select;
const bool draw_fade = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FADE_INACTIVE) &&
overlay_should_fade_object(ob, draw_ctx->obact);
+ const bool draw_mode_transfer = draw_surface;
const bool draw_bones = (pd->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0;
const bool draw_wires = draw_surface && has_surface &&
(pd->wireframe_mode || !pd->hide_overlays);
@@ -353,6 +351,9 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
if (draw_facing) {
OVERLAY_facing_cache_populate(vedata, ob);
}
+ if (draw_mode_transfer) {
+ OVERLAY_mode_transfer_cache_populate(vedata, ob);
+ }
if (draw_wires) {
OVERLAY_wireframe_cache_populate(vedata, ob, dupli, do_init);
}
@@ -508,6 +509,7 @@ static void OVERLAY_cache_finish(void *vedata)
{GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)});
}
+ OVERLAY_mode_transfer_cache_finish(vedata);
OVERLAY_antialiasing_cache_finish(vedata);
OVERLAY_armature_cache_finish(vedata);
OVERLAY_image_cache_finish(vedata);
@@ -570,6 +572,7 @@ static void OVERLAY_draw_scene(void *vedata)
OVERLAY_image_draw(vedata);
OVERLAY_fade_draw(vedata);
OVERLAY_facing_draw(vedata);
+ OVERLAY_mode_transfer_draw(vedata);
OVERLAY_extra_blend_draw(vedata);
OVERLAY_volume_draw(vedata);
@@ -609,6 +612,7 @@ static void OVERLAY_draw_scene(void *vedata)
OVERLAY_fade_infront_draw(vedata);
OVERLAY_facing_infront_draw(vedata);
+ OVERLAY_mode_transfer_infront_draw(vedata);
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_line_in_front_fb);
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 00429a19cf4..2e1c0165513 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -542,16 +542,15 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay
DRW_buffer_add_entry(cb->field_vortex, color, &instdata);
break;
case PFIELD_GUIDE:
- if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->path &&
- ob->runtime.curve_cache->path->data) {
+ if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->anim_path_accum_length) {
instdata.size_x = instdata.size_y = instdata.size_z = pd->f_strength;
float pos[4], tmp[3];
- where_on_path(ob, 0.0f, pos, tmp, NULL, NULL, NULL);
+ BKE_where_on_path(ob, 0.0f, pos, tmp, NULL, NULL, NULL);
copy_v3_v3(instdata.pos, ob->obmat[3]);
translate_m4(instdata.mat, pos[0], pos[1], pos[2]);
DRW_buffer_add_entry(cb->field_curve, color, &instdata);
- where_on_path(ob, 1.0f, pos, tmp, NULL, NULL, NULL);
+ BKE_where_on_path(ob, 1.0f, pos, tmp, NULL, NULL, NULL);
copy_v3_v3(instdata.pos, ob->obmat[3]);
translate_m4(instdata.mat, pos[0], pos[1], pos[2]);
DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata);
diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c
index 891142fe0a2..aa26aa47faa 100644
--- a/source/blender/draw/engines/overlay/overlay_gpencil.c
+++ b/source/blender/draw/engines/overlay/overlay_gpencil.c
@@ -382,7 +382,7 @@ static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl),
void *thunk)
{
Object *ob = (Object *)thunk;
- Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1);
+ Material *ma = BKE_object_material_get_eval(ob, gps->mat_nr + 1);
if (ma == NULL) {
return;
}
diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.c b/source/blender/draw/engines/overlay/overlay_mode_transfer.c
new file mode 100644
index 00000000000..253f606b086
--- /dev/null
+++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.c
@@ -0,0 +1,159 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#include "BKE_paint.h"
+#include "DRW_render.h"
+
+#include "ED_view3d.h"
+
+#include "PIL_time.h"
+#include "UI_resources.h"
+
+#include "overlay_private.h"
+
+void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+
+ pd->mode_transfer.time = PIL_check_seconds_timer();
+
+ for (int i = 0; i < 2; i++) {
+ /* Non Meshes Pass (Camera, empties, lights ...) */
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA;
+ DRW_PASS_CREATE(psl->mode_transfer_ps[i], state | pd->clipping_state);
+ }
+}
+
+#define MODE_TRANSFER_FLASH_LENGTH 0.55f
+/* TODO(pablodp606): Remove this option for 3.0 if fade in/out is not used. */
+#define MODE_TRANSFER_FLASH_FADE 0.0f
+#define MODE_TRANSFER_FLASH_MAX_ALPHA 0.25f
+
+static bool mode_transfer_is_animation_running(const float anim_time)
+{
+ return anim_time >= 0.0f && anim_time <= MODE_TRANSFER_FLASH_LENGTH;
+}
+
+static float mode_transfer_alpha_for_animation_time_get(const float anim_time)
+{
+ if (anim_time > MODE_TRANSFER_FLASH_LENGTH) {
+ return 0.0f;
+ }
+
+ if (anim_time < 0.0f) {
+ return 0.0f;
+ }
+
+ if (MODE_TRANSFER_FLASH_FADE <= 0.0f) {
+ return (1.0f - (anim_time / MODE_TRANSFER_FLASH_LENGTH)) * MODE_TRANSFER_FLASH_MAX_ALPHA;
+ }
+
+ const float flash_fade_in_time = MODE_TRANSFER_FLASH_LENGTH * MODE_TRANSFER_FLASH_FADE;
+ const float flash_fade_out_time = MODE_TRANSFER_FLASH_LENGTH - flash_fade_in_time;
+
+ float alpha = 0.0f;
+ if (anim_time < flash_fade_in_time) {
+ alpha = anim_time / flash_fade_in_time;
+ }
+ else {
+ const float fade_out_anim_time = anim_time - flash_fade_in_time;
+ alpha = 1.0f - (fade_out_anim_time / flash_fade_out_time);
+ }
+
+ return alpha * MODE_TRANSFER_FLASH_MAX_ALPHA;
+}
+
+void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob)
+{
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+ OVERLAY_PassList *psl = vedata->psl;
+
+ if (pd->xray_enabled) {
+ return;
+ }
+
+ const float animation_time = pd->mode_transfer.time -
+ ob->runtime.overlay_mode_transfer_start_time;
+
+ if (!mode_transfer_is_animation_running(animation_time)) {
+ return;
+ }
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
+ !DRW_state_is_image_render();
+ const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
+
+ DRWShadingGroup *mode_transfer_grp[2];
+
+ for (int i = 0; i < 2; i++) {
+ GPUShader *sh = OVERLAY_shader_uniform_color();
+ mode_transfer_grp[i] = DRW_shgroup_create(sh, psl->mode_transfer_ps[i]);
+ DRW_shgroup_uniform_block(mode_transfer_grp[i], "globalsBlock", G_draw.block_ubo);
+
+ float color[4];
+ UI_GetThemeColor3fv(TH_VERTEX_SELECT, color);
+ color[3] = mode_transfer_alpha_for_animation_time_get(animation_time);
+ srgb_to_linearrgb_v4(color, color);
+ DRW_shgroup_uniform_vec4_copy(mode_transfer_grp[i], "color", color);
+ }
+
+ if (!pd->use_in_front) {
+ mode_transfer_grp[IN_FRONT] = mode_transfer_grp[NOT_IN_FRONT];
+ }
+
+ pd->mode_transfer.any_animated = true;
+
+ if (use_sculpt_pbvh) {
+ DRW_shgroup_call_sculpt(mode_transfer_grp[is_xray], ob, false, false);
+ }
+ else {
+ struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
+ if (geom) {
+ DRW_shgroup_call(mode_transfer_grp[is_xray], geom, ob);
+ }
+ }
+}
+
+void OVERLAY_mode_transfer_draw(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+
+ DRW_draw_pass(psl->mode_transfer_ps[NOT_IN_FRONT]);
+}
+
+void OVERLAY_mode_transfer_infront_draw(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+
+ DRW_draw_pass(psl->mode_transfer_ps[IN_FRONT]);
+}
+
+void OVERLAY_mode_transfer_cache_finish(OVERLAY_Data *vedata)
+{
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+ if (pd->mode_transfer.any_animated) {
+ DRW_viewport_request_redraw();
+ }
+ pd->mode_transfer.any_animated = false;
+}
diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c
index 48b7b53a5ba..a92f11aca38 100644
--- a/source/blender/draw/engines/overlay/overlay_motion_path.c
+++ b/source/blender/draw/engines/overlay/overlay_motion_path.c
@@ -190,7 +190,7 @@ static void motion_path_cache(OVERLAY_Data *vedata,
bool is_keyframe = (mpv->flag & MOTIONPATH_VERT_KEY) != 0;
if ((show_keyframes && show_keyframes_no && is_keyframe) || (show_frame_no && (i == 0))) {
- numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame);
+ numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), " %d", frame);
DRW_text_cache_add(
dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, (is_keyframe) ? col_kf : col);
}
@@ -200,7 +200,7 @@ static void motion_path_cache(OVERLAY_Data *vedata,
/* Only draw frame number if several consecutive highlighted points
* don't occur on same point. */
if ((equals_v3v3(mpv->co, mpvP->co) == 0) || (equals_v3v3(mpv->co, mpvN->co) == 0)) {
- numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame);
+ numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), " %d", frame);
DRW_text_cache_add(dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, col);
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c
index 89e724bcfcc..d52640ed174 100644
--- a/source/blender/draw/engines/overlay/overlay_paint.c
+++ b/source/blender/draw/engines/overlay/overlay_paint.c
@@ -48,7 +48,7 @@ static bool paint_object_is_rendered_transparent(View3D *v3d, Object *ob)
v3d->shading.color_type == V3D_SHADING_MATERIAL_COLOR) {
Mesh *me = ob->data;
for (int i = 0; i < me->totcol; i++) {
- Material *mat = me->mat[i];
+ Material *mat = BKE_object_material_get_eval(ob, i + 1);
if (mat && mat->a < 1.0f) {
return true;
}
diff --git a/source/blender/draw/engines/overlay/overlay_particle.c b/source/blender/draw/engines/overlay/overlay_particle.c
index 71064e7ff47..5fa74a8c3a6 100644
--- a/source/blender/draw/engines/overlay/overlay_particle.c
+++ b/source/blender/draw/engines/overlay/overlay_particle.c
@@ -186,7 +186,7 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* TODO(fclem): Here would be a good place for preemptive culling. */
/* NOTE(fclem): Is color even useful in our modern context? */
- Material *ma = BKE_object_material_get(ob, part->omat);
+ Material *ma = BKE_object_material_get_eval(ob, part->omat);
float color[4] = {0.6f, 0.6f, 0.6f, part->draw_size};
if (ma != NULL) {
copy_v3_v3(color, &ma->r);
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h
index db43136e308..969289a3219 100644
--- a/source/blender/draw/engines/overlay/overlay_private.h
+++ b/source/blender/draw/engines/overlay/overlay_private.h
@@ -107,6 +107,7 @@ typedef struct OVERLAY_PassList {
DRWPass *gpencil_canvas_ps;
DRWPass *facing_ps[2];
DRWPass *fade_ps[2];
+ DRWPass *mode_transfer_ps[2];
DRWPass *grid_ps;
DRWPass *image_background_ps;
DRWPass *image_background_scene_ps;
@@ -282,6 +283,7 @@ typedef struct OVERLAY_PrivateData {
DRWShadingGroup *extra_grid_grp;
DRWShadingGroup *facing_grp[2];
DRWShadingGroup *fade_grp[2];
+ DRWShadingGroup *flash_grp[2];
DRWShadingGroup *motion_path_lines_grp;
DRWShadingGroup *motion_path_points_grp;
DRWShadingGroup *outlines_grp;
@@ -414,6 +416,10 @@ typedef struct OVERLAY_PrivateData {
struct {
DRWCallBuffer *handle[2];
} mball;
+ struct {
+ double time;
+ bool any_animated;
+ } mode_transfer;
} OVERLAY_PrivateData; /* Transient data */
typedef struct OVERLAY_StorageList {
@@ -607,6 +613,12 @@ void OVERLAY_fade_cache_populate(OVERLAY_Data *vedata, Object *ob);
void OVERLAY_fade_draw(OVERLAY_Data *vedata);
void OVERLAY_fade_infront_draw(OVERLAY_Data *vedata);
+void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata);
+void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob);
+void OVERLAY_mode_transfer_draw(OVERLAY_Data *vedata);
+void OVERLAY_mode_transfer_infront_draw(OVERLAY_Data *vedata);
+void OVERLAY_mode_transfer_cache_finish(OVERLAY_Data *vedata);
+
void OVERLAY_grid_init(OVERLAY_Data *vedata);
void OVERLAY_grid_cache_init(OVERLAY_Data *vedata);
void OVERLAY_grid_draw(OVERLAY_Data *vedata);
diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c
index 7ba5fb3a426..b8a61ecc403 100644
--- a/source/blender/draw/engines/overlay/overlay_wireframe.c
+++ b/source/blender/draw/engines/overlay/overlay_wireframe.c
@@ -177,8 +177,23 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
const bool all_wires = (ob->dtx & OB_DRAW_ALL_EDGES) != 0;
const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
const bool is_mesh = ob->type == OB_MESH;
- const bool is_mesh_verts_only = is_mesh && (((Mesh *)ob->data)->totedge == 0 &&
- ((Mesh *)ob->data)->totvert > 0);
+ const bool is_edit_mode = DRW_object_is_in_edit_mode(ob);
+ bool has_edit_mesh_cage = false;
+ bool is_mesh_verts_only = false;
+ if (is_mesh) {
+ /* TODO: Should be its own function. */
+ Mesh *me = ob->data;
+ if (is_edit_mode) {
+ BLI_assert(me->edit_mesh);
+ BMEditMesh *embm = me->edit_mesh;
+ has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final);
+ if (embm->mesh_eval_final) {
+ me = embm->mesh_eval_final;
+ }
+ }
+ is_mesh_verts_only = me->totedge == 0 && me->totvert > 0;
+ }
+
const bool use_wire = !is_mesh_verts_only && ((pd->overlay.flag & V3D_OVERLAY_WIREFRAMES) ||
(ob->dtx & OB_DRAWWIRE) || (ob->dt == OB_WIRE));
@@ -261,17 +276,6 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
}
}
- const bool is_edit_mode = DRW_object_is_in_edit_mode(ob);
- bool has_edit_mesh_cage = false;
- if (is_mesh && is_edit_mode) {
- /* TODO: Should be its own function. */
- Mesh *me = (Mesh *)ob->data;
- BMEditMesh *embm = me->edit_mesh;
- if (embm) {
- has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final);
- }
- }
-
/* Don't do that in edit Mesh mode, unless there is a modifier preview. */
if (use_wire && (!is_mesh || (!is_edit_mode || has_edit_mesh_cage))) {
const bool is_sculpt_mode = ((ob->mode & OB_MODE_SCULPT) != 0) && (ob->sculpt != NULL);
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index 6e9118bfe46..800d1085505 100644
--- a/source/blender/draw/engines/workbench/workbench_materials.c
+++ b/source/blender/draw/engines/workbench/workbench_materials.c
@@ -93,7 +93,7 @@ void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd,
/* Return correct material or empty default material if slot is empty. */
BLI_INLINE Material *workbench_object_material_get(Object *ob, int mat_nr)
{
- Material *ma = BKE_object_material_get(ob, mat_nr);
+ Material *ma = BKE_object_material_get_eval(ob, mat_nr);
if (ma == NULL) {
ma = BKE_material_default_empty();
}
diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c
index c76f4a4c470..ddda6d7b58e 100644
--- a/source/blender/draw/engines/workbench/workbench_volume.c
+++ b/source/blender/draw/engines/workbench/workbench_volume.c
@@ -202,7 +202,7 @@ static void workbench_volume_material_color(WORKBENCH_PrivateData *wpd,
eV3DShadingColorType color_type,
float color[3])
{
- Material *ma = BKE_object_material_get(ob, VOLUME_MATERIAL_NR);
+ Material *ma = BKE_object_material_get_eval(ob, VOLUME_MATERIAL_NR);
WORKBENCH_UBO_Material ubo_data;
workbench_material_ubo_data(wpd, ob, ma, &ubo_data, color_type);
copy_v3_v3(color, ubo_data.base_color);
@@ -215,7 +215,7 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata,
/* Create 3D textures. */
Volume *volume = ob->data;
BKE_volume_load(volume, G.main);
- VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume);
+ const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume);
if (volume_grid == NULL) {
return;
}
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 2545cfa65dc..5071658fd82 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -438,6 +438,10 @@ void DRW_shgroup_call_range(
void DRW_shgroup_call_instance_range(
DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct);
+void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
+ int groups_x_len,
+ int groups_y_len,
+ int groups_z_len);
void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count);
void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count);
void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count);
@@ -575,6 +579,9 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup,
const char *name,
const float (*value)[4],
int arraysize);
+void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
+ const char *name,
+ struct GPUVertBuf *vertex_buffer);
bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup);
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index c15c246992d..c58dd85b6ed 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -244,7 +244,8 @@ typedef struct DRWVolumeGrid {
float bounds_to_texture[4][4];
} DRWVolumeGrid;
-DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume, struct VolumeGrid *grid);
+DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume,
+ const struct VolumeGrid *grid);
struct GPUBatch *DRW_cache_volume_face_wireframe_get(struct Object *ob);
struct GPUBatch *DRW_cache_volume_selection_surface_get(struct Object *ob);
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index c929fe7dfd3..304cae1d2dc 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -24,6 +24,10 @@
struct TaskGraph;
+#include "GPU_batch.h"
+#include "GPU_index_buffer.h"
+#include "GPU_vertex_buffer.h"
+
/* Vertex Group Selection and display options */
typedef struct DRW_MeshWeightState {
int defgroup_active;
@@ -64,30 +68,34 @@ typedef struct DRW_MeshCDMask {
* bit-wise and atomic operations are used to compare and update the struct.
* See `mesh_cd_layers_type_*` functions. */
BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits")
-
typedef enum eMRIterType {
MR_ITER_LOOPTRI = 1 << 0,
MR_ITER_POLY = 1 << 1,
MR_ITER_LEDGE = 1 << 2,
MR_ITER_LVERT = 1 << 3,
} eMRIterType;
+ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT)
typedef enum eMRDataType {
+ MR_DATA_NONE = 0,
MR_DATA_POLY_NOR = 1 << 1,
MR_DATA_LOOP_NOR = 1 << 2,
MR_DATA_LOOPTRI = 1 << 3,
/** Force loop normals calculation. */
MR_DATA_TAN_LOOP_NOR = 1 << 4,
} eMRDataType;
+ENUM_OPERATORS(eMRDataType, MR_DATA_TAN_LOOP_NOR)
-typedef enum eMRExtractType {
- MR_EXTRACT_BMESH,
- MR_EXTRACT_MAPPED,
- MR_EXTRACT_MESH,
-} eMRExtractType;
+#ifdef __cplusplus
+extern "C" {
+#endif
-BLI_INLINE int mesh_render_mat_len_get(Mesh *me)
+BLI_INLINE int mesh_render_mat_len_get(const Mesh *me)
{
+ /* In edit mode, the displayed mesh is stored in the edit-mesh. */
+ if (me->edit_mesh && me->edit_mesh->mesh_eval_final) {
+ return MAX2(1, me->edit_mesh->mesh_eval_final->totcol);
+ }
return MAX2(1, me->totcol);
}
@@ -146,6 +154,18 @@ typedef struct MeshBufferCache {
GPUIndexBuf **tris_per_mat;
} MeshBufferCache;
+/**
+ * Data that are kept around between extractions to reduce rebuilding time.
+ *
+ * - Loose geometry.
+ */
+typedef struct MeshBufferExtractionCache {
+ int edge_loose_len;
+ int vert_loose_len;
+ int *lverts;
+ int *ledges;
+} MeshBufferExtractionCache;
+
typedef enum DRWBatchFlag {
MBC_SURFACE = (1 << 0),
MBC_SURFACE_WEIGHTS = (1 << 1),
@@ -191,6 +211,10 @@ typedef enum DRWBatchFlag {
typedef struct MeshBatchCache {
MeshBufferCache final, cage, uv_cage;
+ MeshBufferExtractionCache final_extraction_cache;
+ MeshBufferExtractionCache cage_extraction_cache;
+ MeshBufferExtractionCache uv_cage_extraction_cache;
+
struct {
/* Surfaces / Render */
GPUBatch *surface;
@@ -260,9 +284,14 @@ typedef struct MeshBatchCache {
bool no_loose_wire;
} MeshBatchCache;
+#define MBC_BATCH_LEN (sizeof(((MeshBatchCache){0}).batch) / sizeof(void *))
+#define MBC_VBO_LEN (sizeof(((MeshBufferCache){0}).vbo) / sizeof(void *))
+#define MBC_IBO_LEN (sizeof(((MeshBufferCache){0}).ibo) / sizeof(void *))
+
void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshBatchCache *cache,
- MeshBufferCache mbc,
+ MeshBufferCache *mbc,
+ MeshBufferExtractionCache *extraction_cache,
Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
@@ -271,7 +300,10 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
const bool do_final,
const bool do_uvedit,
const bool use_subsurf_fdots,
- const DRW_MeshCDMask *cd_layer_used,
const Scene *scene,
const ToolSettings *ts,
const bool use_hide);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
deleted file mode 100644
index f167ea3d540..00000000000
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ /dev/null
@@ -1,6165 +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) 2017 by Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup draw
- *
- * \brief Extraction of Mesh data into VBO to feed to GPU.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_alloca.h"
-#include "BLI_bitmap.h"
-#include "BLI_buffer.h"
-#include "BLI_edgehash.h"
-#include "BLI_jitter_2d.h"
-#include "BLI_math_bits.h"
-#include "BLI_math_vector.h"
-#include "BLI_string.h"
-#include "BLI_task.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-
-#include "BKE_bvhutils.h"
-#include "BKE_customdata.h"
-#include "BKE_deform.h"
-#include "BKE_editmesh.h"
-#include "BKE_editmesh_bvh.h"
-#include "BKE_editmesh_cache.h"
-#include "BKE_editmesh_tangent.h"
-#include "BKE_mesh.h"
-#include "BKE_mesh_runtime.h"
-#include "BKE_mesh_tangent.h"
-#include "BKE_modifier.h"
-#include "BKE_object_deform.h"
-#include "BKE_paint.h"
-
-#include "atomic_ops.h"
-
-#include "bmesh.h"
-
-#include "GPU_batch.h"
-#include "GPU_capabilities.h"
-
-#include "DRW_render.h"
-
-#include "ED_mesh.h"
-#include "ED_uvedit.h"
-
-#include "draw_cache_impl.h"
-#include "draw_cache_inline.h"
-
-#include "draw_cache_extract.h"
-
-// #define DEBUG_TIME
-
-#ifdef DEBUG_TIME
-# include "PIL_time_utildefines.h"
-#endif
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
- * \{ */
-
-typedef struct MeshRenderData {
- eMRExtractType extract_type;
-
- int poly_len, edge_len, vert_len, loop_len;
- int edge_loose_len;
- int vert_loose_len;
- int loop_loose_len;
- int tri_len;
- int mat_len;
-
- bool use_hide;
- bool use_subsurf_fdots;
- bool use_final_mesh;
-
- /** Use for #MeshStatVis calculation which use world-space coords. */
- float obmat[4][4];
-
- const ToolSettings *toolsettings;
- /** Edit Mesh */
- BMEditMesh *edit_bmesh;
- BMesh *bm;
- EditMeshData *edit_data;
-
- /* For deformed edit-mesh data. */
- /* Use for #ME_WRAPPER_TYPE_BMESH. */
- const float (*bm_vert_coords)[3];
- const float (*bm_vert_normals)[3];
- const float (*bm_poly_normals)[3];
- const float (*bm_poly_centers)[3];
-
- int *v_origindex, *e_origindex, *p_origindex;
- int crease_ofs;
- int bweight_ofs;
- int freestyle_edge_ofs;
- int freestyle_face_ofs;
- /** Mesh */
- Mesh *me;
- const MVert *mvert;
- const MEdge *medge;
- const MLoop *mloop;
- const MPoly *mpoly;
- BMVert *eve_act;
- BMEdge *eed_act;
- BMFace *efa_act;
- BMFace *efa_act_uv;
- /* Data created on-demand (usually not for #BMesh based data). */
- MLoopTri *mlooptri;
- float (*loop_normals)[3];
- float (*poly_normals)[3];
- int *lverts, *ledges;
-} MeshRenderData;
-
-static void mesh_render_data_update_loose_geom(MeshRenderData *mr,
- const eMRIterType iter_type,
- const eMRDataType UNUSED(data_flag))
-{
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
- mr->vert_loose_len = 0;
- mr->edge_loose_len = 0;
-
- BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
-
- mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__);
- const MEdge *med = mr->medge;
- for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) {
- if (med->flag & ME_LOOSEEDGE) {
- mr->ledges[mr->edge_loose_len++] = med_index;
- }
- /* Tag verts as not loose. */
- BLI_BITMAP_ENABLE(lvert_map, med->v1);
- BLI_BITMAP_ENABLE(lvert_map, med->v2);
- }
- if (mr->edge_loose_len < mr->edge_len) {
- mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
- }
-
- mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
- for (int v = 0; v < mr->vert_len; v++) {
- if (!BLI_BITMAP_TEST(lvert_map, v)) {
- mr->lverts[mr->vert_loose_len++] = v;
- }
- }
- if (mr->vert_loose_len < mr->vert_len) {
- mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
- }
-
- MEM_freeN(lvert_map);
-
- mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2);
- }
- }
- else {
- /* #BMesh */
- BMesh *bm = mr->bm;
- if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
- int elem_id;
- BMIter iter;
- BMVert *eve;
- BMEdge *ede;
- mr->vert_loose_len = 0;
- mr->edge_loose_len = 0;
-
- mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) {
- if (eve->e == NULL) {
- mr->lverts[mr->vert_loose_len++] = elem_id;
- }
- }
- if (mr->vert_loose_len < mr->vert_len) {
- mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
- }
-
- mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__);
- BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) {
- if (ede->l == NULL) {
- mr->ledges[mr->edge_loose_len++] = elem_id;
- }
- }
- if (mr->edge_loose_len < mr->edge_len) {
- mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
- }
-
- mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
- }
- }
-}
-
-/**
- * Part of the creation of the #MeshRenderData that happens in a thread.
- */
-static void mesh_render_data_update_looptris(MeshRenderData *mr,
- const eMRIterType iter_type,
- const eMRDataType data_flag)
-{
- Mesh *me = mr->me;
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
- mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI");
- BKE_mesh_recalc_looptri(
- me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri);
- }
- }
- else {
- /* #BMesh */
- if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
- /* Edit mode ensures this is valid, no need to calculate. */
- BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL));
- }
- }
-}
-
-static void mesh_render_data_update_normals(MeshRenderData *mr,
- const eMRIterType UNUSED(iter_type),
- const eMRDataType data_flag)
-{
- Mesh *me = mr->me;
- const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
- const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
-
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
- mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__);
- BKE_mesh_calc_normals_poly((MVert *)mr->mvert,
- NULL,
- mr->vert_len,
- mr->mloop,
- mr->mpoly,
- mr->loop_len,
- mr->poly_len,
- mr->poly_normals,
- true);
- }
- if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
- mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
- short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL);
- BKE_mesh_normals_loop_split(mr->me->mvert,
- mr->vert_len,
- mr->me->medge,
- mr->edge_len,
- mr->me->mloop,
- mr->loop_normals,
- mr->loop_len,
- mr->me->mpoly,
- mr->poly_normals,
- mr->poly_len,
- is_auto_smooth,
- split_angle,
- NULL,
- clnors,
- NULL);
- }
- }
- else {
- /* #BMesh */
- if (data_flag & MR_DATA_POLY_NOR) {
- /* Use #BMFace.no instead. */
- }
- if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
-
- const float(*vert_coords)[3] = NULL;
- const float(*vert_normals)[3] = NULL;
- const float(*poly_normals)[3] = NULL;
-
- if (mr->edit_data && mr->edit_data->vertexCos) {
- vert_coords = mr->bm_vert_coords;
- vert_normals = mr->bm_vert_normals;
- poly_normals = mr->bm_poly_normals;
- }
-
- mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
- const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
- BM_loops_calc_normal_vcos(mr->bm,
- vert_coords,
- vert_normals,
- poly_normals,
- is_auto_smooth,
- split_angle,
- mr->loop_normals,
- NULL,
- NULL,
- clnors_offset,
- false);
- }
- }
-}
-
-/**
- * \param is_mode_active: When true, use the modifiers from the edit-data,
- * otherwise don't use modifiers as they are not from this object.
- */
-static MeshRenderData *mesh_render_data_create(Mesh *me,
- const bool is_editmode,
- const bool is_paint_mode,
- const bool is_mode_active,
- const float obmat[4][4],
- const bool do_final,
- const bool do_uvedit,
- const DRW_MeshCDMask *UNUSED(cd_used),
- const ToolSettings *ts,
- const eMRIterType iter_type,
- const eMRDataType data_flag)
-{
- MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__);
- mr->toolsettings = ts;
- mr->mat_len = mesh_render_mat_len_get(me);
-
- copy_m4_m4(mr->obmat, obmat);
-
- if (is_editmode) {
- BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final);
- mr->bm = me->edit_mesh->bm;
- mr->edit_bmesh = me->edit_mesh;
- mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage;
- mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL;
-
- if (mr->edit_data) {
- EditMeshData *emd = mr->edit_data;
- if (emd->vertexCos) {
- BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd);
- BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd);
- }
-
- mr->bm_vert_coords = mr->edit_data->vertexCos;
- mr->bm_vert_normals = mr->edit_data->vertexNos;
- mr->bm_poly_normals = mr->edit_data->polyNos;
- mr->bm_poly_centers = mr->edit_data->polyCos;
- }
-
- bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA);
- bool use_mapped = is_mode_active &&
- (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original);
-
- int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE;
-
- BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types);
- BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP);
-
- mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false);
- mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true);
- mr->eed_act = BM_mesh_active_edge_get(mr->bm);
- mr->eve_act = BM_mesh_active_vert_get(mr->bm);
-
- mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE);
- mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT);
-#ifdef WITH_FREESTYLE
- mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE);
- mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE);
-#endif
-
- if (use_mapped) {
- mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
- mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
- mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
-
- use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
- }
-
- mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH;
-
- /* Seems like the mesh_eval_final do not have the right origin indices.
- * Force not mapped in this case. */
- if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) {
- // mr->edit_bmesh = NULL;
- mr->extract_type = MR_EXTRACT_MESH;
- }
- }
- else {
- mr->me = me;
- mr->edit_bmesh = NULL;
-
- bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original;
- if (use_mapped) {
- mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
- mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
- mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
-
- use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
- }
-
- mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH;
- }
-
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- mr->vert_len = mr->me->totvert;
- mr->edge_len = mr->me->totedge;
- mr->loop_len = mr->me->totloop;
- mr->poly_len = mr->me->totpoly;
- mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
-
- mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT);
- mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE);
- mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP);
- mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY);
-
- mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
- mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
- mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
- }
- else {
- /* #BMesh */
- BMesh *bm = mr->bm;
-
- mr->vert_len = bm->totvert;
- mr->edge_len = bm->totedge;
- mr->loop_len = bm->totloop;
- mr->poly_len = bm->totface;
- mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
- }
- mesh_render_data_update_loose_geom(mr, iter_type, data_flag);
-
- return mr;
-}
-
-static void mesh_render_data_free(MeshRenderData *mr)
-{
- MEM_SAFE_FREE(mr->mlooptri);
- MEM_SAFE_FREE(mr->poly_normals);
- MEM_SAFE_FREE(mr->loop_normals);
-
- MEM_SAFE_FREE(mr->lverts);
- MEM_SAFE_FREE(mr->ledges);
-
- MEM_freeN(mr);
-}
-
-BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
-{
- return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
- BM_face_at_index(mr->bm, mr->p_origindex[idx]) :
- NULL;
-}
-
-BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx)
-{
- return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
- BM_edge_at_index(mr->bm, mr->e_origindex[idx]) :
- NULL;
-}
-
-BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx)
-{
- return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
- BM_vert_at_index(mr->bm, mr->v_origindex[idx]) :
- NULL;
-}
-
-BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve)
-{
- const float(*vert_coords)[3] = mr->bm_vert_coords;
- if (vert_coords != NULL) {
- return vert_coords[BM_elem_index_get(eve)];
- }
-
- UNUSED_VARS(mr);
- return eve->co;
-}
-
-BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve)
-{
- const float(*vert_normals)[3] = mr->bm_vert_normals;
- if (vert_normals != NULL) {
- return vert_normals[BM_elem_index_get(eve)];
- }
-
- UNUSED_VARS(mr);
- return eve->no;
-}
-
-BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa)
-{
- const float(*poly_normals)[3] = mr->bm_poly_normals;
- if (poly_normals != NULL) {
- return poly_normals[BM_elem_index_get(efa)];
- }
-
- UNUSED_VARS(mr);
- return efa->no;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract: Loop Triangles
- * \{ */
-
-typedef struct ExtractTriBMesh_Params {
- BMLoop *(*looptris)[3];
- int tri_range[2];
-} ExtractTriBMesh_Params;
-typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr,
- const ExtractTriBMesh_Params *params,
- void *data);
-
-#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \
- CHECK_TYPE(params, const ExtractTriBMesh_Params *); \
- { \
- const int _tri_index_end = (params)->tri_range[1]; \
- BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \
- for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \
- index_tri += 1, elem_tri += 3)
-#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END }
-
-typedef struct ExtractTriMesh_Params {
- const MLoopTri *mlooptri;
- int tri_range[2];
-} ExtractTriMesh_Params;
-typedef void(ExtractTriMeshFn)(const MeshRenderData *mr,
- const ExtractTriMesh_Params *params,
- void *data);
-
-#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \
- CHECK_TYPE(params, const ExtractTriMesh_Params *); \
- { \
- const int _tri_index_end = (params)->tri_range[1]; \
- const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \
- for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \
- index_tri += 1, elem_tri += 1)
-#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END }
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract: Polygons, Loops
- * \{ */
-
-typedef struct ExtractPolyBMesh_Params {
- BMLoop *(*looptris)[3];
- int poly_range[2];
-} ExtractPolyBMesh_Params;
-typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data);
-
-#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \
- CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \
- { \
- BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
- BMFace **_ftable = mr->bm->ftable; \
- const int _poly_index_end = (params)->poly_range[1]; \
- for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
- index_poly += 1) { \
- BMFace *elem_poly = _ftable[index_poly]; \
- (void)elem_poly;
-
-#define EXTRACT_POLY_FOREACH_BM_END \
- } \
- }
-
-/* Iterate over polygon and loop. */
-#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \
- CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \
- { \
- BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
- BMFace **_ftable = mr->bm->ftable; \
- const int _poly_index_end = (params)->poly_range[1]; \
- for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
- index_poly += 1) { \
- BMFace *elem_face = _ftable[index_poly]; \
- BMLoop *elem_loop, *l_first; \
- elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \
- do { \
- const int index_loop = BM_elem_index_get(elem_loop); \
- (void)index_loop; /* Quiet warning when unused. */
-
-#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \
- } \
- while ((elem_loop = elem_loop->next) != l_first) \
- ; \
- } \
- }
-
-typedef struct ExtractPolyMesh_Params {
- int poly_range[2];
-} ExtractPolyMesh_Params;
-typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data);
-
-#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \
- CHECK_TYPE(params, const ExtractPolyMesh_Params *); \
- { \
- const MPoly *_mpoly = mr->mpoly; \
- const int _poly_index_end = (params)->poly_range[1]; \
- for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
- index_poly += 1) { \
- const MPoly *elem_poly = &_mpoly[index_poly]; \
- (void)elem_poly;
-
-#define EXTRACT_POLY_FOREACH_MESH_END \
- } \
- }
-
-/* Iterate over polygon and loop. */
-#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \
- elem_poly, index_poly, elem_loop, index_loop, params, mr) \
- CHECK_TYPE(params, const ExtractPolyMesh_Params *); \
- { \
- const MPoly *_mpoly = mr->mpoly; \
- const MLoop *_mloop = mr->mloop; \
- const int _poly_index_end = (params)->poly_range[1]; \
- for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
- index_poly += 1) { \
- const MPoly *elem_poly = &_mpoly[index_poly]; \
- const int _index_end = elem_poly->loopstart + elem_poly->totloop; \
- for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \
- const MLoop *elem_loop = &_mloop[index_loop]; \
- (void)elem_loop;
-
-#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \
- } \
- } \
- }
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract: Loose Edges
- * \{ */
-
-typedef struct ExtractLEdgeBMesh_Params {
- const int *ledge;
- int ledge_range[2];
-} ExtractLEdgeBMesh_Params;
-typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *data);
-
-#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \
- CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \
- { \
- BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \
- BMEdge **_etable = mr->bm->etable; \
- const int *_ledge = (params)->ledge; \
- const int _ledge_index_end = (params)->ledge_range[1]; \
- for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \
- index_ledge += 1) { \
- BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \
- (void)elem_edge; /* Quiet warning when unused. */ \
- {
-#define EXTRACT_LEDGE_FOREACH_BM_END \
- } \
- } \
- }
-
-typedef struct ExtractLEdgeMesh_Params {
- const int *ledge;
- int ledge_range[2];
-} ExtractLEdgeMesh_Params;
-typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *data);
-
-#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \
- CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \
- { \
- const MEdge *_medge = mr->medge; \
- const int *_ledge = (params)->ledge; \
- const int _ledge_index_end = (params)->ledge_range[1]; \
- for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \
- index_ledge += 1) { \
- const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \
- (void)elem_edge; /* Quiet warning when unused. */ \
- {
-#define EXTRACT_LEDGE_FOREACH_MESH_END \
- } \
- } \
- }
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract: Loose Vertices
- * \{ */
-
-typedef struct ExtractLVertBMesh_Params {
- const int *lvert;
- int lvert_range[2];
-} ExtractLVertBMesh_Params;
-typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *data);
-
-#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \
- CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \
- { \
- BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
- BMVert **vtable = mr->bm->vtable; \
- const int *lverts = (params)->lvert; \
- const int _lvert_index_end = (params)->lvert_range[1]; \
- for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \
- index_lvert += 1) { \
- BMVert *elem_vert = vtable[lverts[index_lvert]]; \
- (void)elem_vert; /* Quiet warning when unused. */ \
- {
-#define EXTRACT_LVERT_FOREACH_BM_END \
- } \
- } \
- }
-
-typedef struct ExtractLVertMesh_Params {
- const int *lvert;
- int lvert_range[2];
-} ExtractLVertMesh_Params;
-typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *data);
-
-#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \
- CHECK_TYPE(params, const ExtractLVertMesh_Params *); \
- { \
- const MVert *mvert = mr->mvert; \
- const int *lverts = (params)->lvert; \
- const int _lvert_index_end = (params)->lvert_range[1]; \
- for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \
- index_lvert += 1) { \
- const MVert *elem = &mvert[lverts[index_lvert]]; \
- (void)elem; /* Quiet warning when unused. */ \
- {
-#define EXTRACT_LVERT_FOREACH_MESH_END \
- } \
- } \
- }
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract Struct
- * \{ */
-
-typedef void *(ExtractInitFn)(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *buffer);
-typedef void(ExtractFinishFn)(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *buffer,
- void *data);
-
-typedef struct MeshExtract {
- /** Executed on main thread and return user data for iteration functions. */
- ExtractInitFn *init;
- /** Executed on one (or more if use_threading) worker thread(s). */
- ExtractTriBMeshFn *iter_looptri_bm;
- ExtractTriMeshFn *iter_looptri_mesh;
- ExtractPolyBMeshFn *iter_poly_bm;
- ExtractPolyMeshFn *iter_poly_mesh;
- ExtractLEdgeBMeshFn *iter_ledge_bm;
- ExtractLEdgeMeshFn *iter_ledge_mesh;
- ExtractLVertBMeshFn *iter_lvert_bm;
- ExtractLVertMeshFn *iter_lvert_mesh;
- /** Executed on one worker thread after all elements iterations. */
- ExtractFinishFn *finish;
- /** Used to request common data. */
- const eMRDataType data_flag;
- /** Used to know if the element callbacks are thread-safe and can be parallelized. */
- const bool use_threading;
-} MeshExtract;
-
-BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext)
-{
- eMRIterType type = 0;
- SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI);
- SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY);
- SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE);
- SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT);
- return type;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Triangles Indices
- * \{ */
-
-typedef struct MeshExtract_Tri_Data {
- GPUIndexBufBuilder elb;
- int *tri_mat_start;
- int *tri_mat_end;
-} MeshExtract_Tri_Data;
-
-static void *extract_tris_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__);
-
- size_t mat_tri_idx_size = sizeof(int) * mr->mat_len;
- data->tri_mat_start = MEM_callocN(mat_tri_idx_size, __func__);
- data->tri_mat_end = MEM_callocN(mat_tri_idx_size, __func__);
-
- int *mat_tri_len = data->tri_mat_start;
- /* Count how many triangle for each material. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
- int mat = min_ii(efa->mat_nr, mr->mat_len - 1);
- mat_tri_len[mat] += efa->len - 2;
- }
- }
- }
- else {
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
- mat_tri_len[mat] += mp->totloop - 2;
- }
- }
- }
- /* Accumulate triangle lengths per material to have correct offsets. */
- int ofs = mat_tri_len[0];
- mat_tri_len[0] = 0;
- for (int i = 1; i < mr->mat_len; i++) {
- int tmp = mat_tri_len[i];
- mat_tri_len[i] = ofs;
- ofs += tmp;
- }
-
- memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size);
-
- int visible_tri_tot = ofs;
- GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len);
-
- return data;
-}
-
-static void extract_tris_iter_looptri_bm(const MeshRenderData *mr,
- const struct ExtractTriBMesh_Params *params,
- void *_data)
-{
- MeshExtract_Tri_Data *data = _data;
- const int mat_last = mr->mat_len - 1;
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
- {
- if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
- int *mat_tri_ofs = data->tri_mat_end;
- const int mat = min_ii(elt[0]->f->mat_nr, mat_last);
- GPU_indexbuf_set_tri_verts(&data->elb,
- mat_tri_ofs[mat]++,
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]));
- }
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
-}
-
-static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr,
- const struct ExtractTriMesh_Params *params,
- void *_data)
-{
- MeshExtract_Tri_Data *data = _data;
- const int mat_last = mr->mat_len - 1;
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
- {
- const MPoly *mp = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- int *mat_tri_ofs = data->tri_mat_end;
- const int mat = min_ii(mp->mat_nr, mat_last);
- GPU_indexbuf_set_tri_verts(
- &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
- }
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
-}
-
-static void extract_tris_finish(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *ibo,
- void *_data)
-{
- MeshExtract_Tri_Data *data = _data;
- GPU_indexbuf_build_in_place(&data->elb, ibo);
-
- /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
- * is created before the surfaces-per-material. */
- if (mr->use_final_mesh && cache->final.tris_per_mat) {
- MeshBufferCache *mbc = &cache->final;
- for (int i = 0; i < mr->mat_len; i++) {
- /* These IBOs have not been queried yet but we create them just in case they are needed
- * later since they are not tracked by mesh_buffer_cache_create_requested(). */
- if (mbc->tris_per_mat[i] == NULL) {
- mbc->tris_per_mat[i] = GPU_indexbuf_calloc();
- }
- /* Multiply by 3 because these are triangle indices. */
- const int mat_start = data->tri_mat_start[i];
- const int mat_end = data->tri_mat_end[i];
- const int start = mat_start * 3;
- const int len = (mat_end - mat_start) * 3;
- GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, start, len);
- }
- }
- MEM_freeN(data->tri_mat_start);
- MEM_freeN(data->tri_mat_end);
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_tris = {
- .init = extract_tris_init,
- .iter_looptri_bm = extract_tris_iter_looptri_bm,
- .iter_looptri_mesh = extract_tris_iter_looptri_mesh,
- .finish = extract_tris_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edges Indices
- * \{ */
-
-static void *extract_lines_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
- /* Put loose edges at the end. */
- GPU_indexbuf_init(
- elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len);
- return elb;
-}
-
-static void extract_lines_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *elb)
-{
- /* Using poly & loop iterator would complicate accessing the adjacent loop. */
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- BMLoop *l_iter, *l_first;
- /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */
- l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev;
- do {
- if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_line_verts(elb,
- BM_elem_index_get(l_iter->e),
- BM_elem_index_get(l_iter),
- BM_elem_index_get(l_iter->next));
- }
- else {
- GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e));
- }
- } while ((l_iter = l_iter->next) != l_first);
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_lines_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *elb)
-{
- /* Using poly & loop iterator would complicate accessing the adjacent loop. */
- const MLoop *mloop = mr->mloop;
- const MEdge *medge = mr->medge;
- if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- const int ml_index_last = mp->loopstart + (mp->totloop - 1);
- int ml_index = ml_index_last, ml_index_next = mp->loopstart;
- do {
- const MLoop *ml = &mloop[ml_index];
- const MEdge *med = &medge[ml->e];
- if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) {
- GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
- }
- else {
- GPU_indexbuf_set_line_restart(elb, ml->e);
- }
- } while ((ml_index = ml_index_next++) != ml_index_last);
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- const int ml_index_last = mp->loopstart + (mp->totloop - 1);
- int ml_index = ml_index_last, ml_index_next = mp->loopstart;
- do {
- const MLoop *ml = &mloop[ml_index];
- GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
- } while ((ml_index = ml_index_next++) != ml_index_last);
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static void extract_lines_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *elb)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- const int l_index_offset = mr->edge_len + ledge_index;
- if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
- const int l_index = mr->loop_len + ledge_index * 2;
- GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
- }
- else {
- GPU_indexbuf_set_line_restart(elb, l_index_offset);
- }
- /* Don't render the edge twice. */
- GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed));
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *elb)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- const int l_index_offset = mr->edge_len + ledge_index;
- const int e_index = mr->ledges[ledge_index];
- if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
- const int l_index = mr->loop_len + ledge_index * 2;
- GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
- }
- else {
- GPU_indexbuf_set_line_restart(elb, l_index_offset);
- }
- /* Don't render the edge twice. */
- GPU_indexbuf_set_line_restart(elb, e_index);
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_lines_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *elb)
-{
- GPU_indexbuf_build_in_place(elb, ibo);
- MEM_freeN(elb);
-}
-
-static const MeshExtract extract_lines = {
- .init = extract_lines_init,
- .iter_poly_bm = extract_lines_iter_poly_bm,
- .iter_poly_mesh = extract_lines_iter_poly_mesh,
- .iter_ledge_bm = extract_lines_iter_ledge_bm,
- .iter_ledge_mesh = extract_lines_iter_ledge_mesh,
- .finish = extract_lines_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Loose Edges Sub Buffer
- * \{ */
-
-static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache)
-{
- BLI_assert(cache->final.ibo.lines);
- /* Multiply by 2 because these are edges indices. */
- const int start = mr->edge_len * 2;
- const int len = mr->edge_loose_len * 2;
- GPU_indexbuf_create_subrange_in_place(
- cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len);
- cache->no_loose_wire = (len == 0);
-}
-
-static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *ibo,
- void *elb)
-{
- GPU_indexbuf_build_in_place(elb, ibo);
- extract_lines_loose_subbuffer(mr, cache);
- MEM_freeN(elb);
-}
-
-static const MeshExtract extract_lines_with_lines_loose = {
- .init = extract_lines_init,
- .iter_poly_bm = extract_lines_iter_poly_bm,
- .iter_poly_mesh = extract_lines_iter_poly_mesh,
- .iter_ledge_bm = extract_lines_iter_ledge_bm,
- .iter_ledge_mesh = extract_lines_iter_ledge_mesh,
- .finish = extract_lines_with_lines_loose_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Point Indices
- * \{ */
-
-static void *extract_points_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
- GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len);
- return elb;
-}
-
-BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index)
-{
- const int v_index = BM_elem_index_get(eve);
- if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_point_vert(elb, v_index, l_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, v_index);
- }
-}
-
-BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
- const MeshRenderData *mr,
- const int v_index,
- const int l_index)
-{
- const MVert *mv = &mr->mvert[v_index];
- if (!((mr->use_hide && (mv->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) {
- GPU_indexbuf_set_point_vert(elb, v_index, l_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, v_index);
- }
-}
-
-static void extract_points_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *elb)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- vert_set_bm(elb, l->v, l_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_points_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *elb)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- vert_set_mesh(elb, mr, ml->v, ml_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_points_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *elb)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2));
- vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1);
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_points_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *elb)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2));
- vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1);
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_points_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *elb)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- vert_set_bm(elb, eve, offset + lvert_index);
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_points_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *elb)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
- {
- vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index);
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static void extract_points_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *elb)
-{
- GPU_indexbuf_build_in_place(elb, ibo);
- MEM_freeN(elb);
-}
-
-static const MeshExtract extract_points = {
- .init = extract_points_init,
- .iter_poly_bm = extract_points_iter_poly_bm,
- .iter_poly_mesh = extract_points_iter_poly_mesh,
- .iter_ledge_bm = extract_points_iter_ledge_bm,
- .iter_ledge_mesh = extract_points_iter_ledge_mesh,
- .iter_lvert_bm = extract_points_iter_lvert_bm,
- .iter_lvert_mesh = extract_points_iter_lvert_mesh,
- .finish = extract_points_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Indices
- * \{ */
-
-static void *extract_fdots_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
- GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
- return elb;
-}
-
-static void extract_fdots_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *elb)
-{
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_point_vert(elb, f_index, f_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, f_index);
- }
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *elb)
-{
- if (mr->use_subsurf_fdots) {
- /* Check #ME_VERT_FACEDOT. */
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MVert *mv = &mr->mvert[ml->v];
- if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) {
- GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, mp_index);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, mp_index);
- }
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static void extract_fdots_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *elb)
-{
- GPU_indexbuf_build_in_place(elb, ibo);
- MEM_freeN(elb);
-}
-
-static const MeshExtract extract_fdots = {
- .init = extract_fdots_init,
- .iter_poly_bm = extract_fdots_iter_poly_bm,
- .iter_poly_mesh = extract_fdots_iter_poly_mesh,
- .finish = extract_fdots_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Paint Mask Line Indices
- * \{ */
-
-typedef struct MeshExtract_LinePaintMask_Data {
- GPUIndexBufBuilder elb;
- /** One bit per edge set if face is selected. */
- BLI_bitmap select_map[0];
-} MeshExtract_LinePaintMask_Data;
-
-static void *extract_lines_paint_mask_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len);
- MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len);
- return data;
-}
-
-static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_LinePaintMask_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const int e_index = ml->e;
- const MEdge *me = &mr->medge[e_index];
- if (!((mr->use_hide && (me->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
-
- const int ml_index_last = mp->totloop + mp->loopstart - 1;
- const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
- if (mp->flag & ME_FACE_SEL) {
- if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) {
- /* Hide edge as it has more than 2 selected loop. */
- GPU_indexbuf_set_line_restart(&data->elb, e_index);
- }
- else {
- /* First selected loop. Set edge visible, overwriting any unselected loop. */
- GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
- }
- }
- else {
- /* Set these unselected loop only if this edge has no other selected loop. */
- if (!BLI_BITMAP_TEST(data->select_map, e_index)) {
- GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
- }
- }
- }
- else {
- GPU_indexbuf_set_line_restart(&data->elb, e_index);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *_data)
-{
- MeshExtract_LinePaintMask_Data *data = _data;
-
- GPU_indexbuf_build_in_place(&data->elb, ibo);
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_lines_paint_mask = {
- .init = extract_lines_paint_mask_init,
- .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh,
- .finish = extract_lines_paint_mask_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Line Adjacency Indices
- * \{ */
-
-#define NO_EDGE INT_MAX
-
-typedef struct MeshExtract_LineAdjacency_Data {
- GPUIndexBufBuilder elb;
- EdgeHash *eh;
- bool is_manifold;
- /* Array to convert vert index to any loop index of this vert. */
- uint vert_to_loop[0];
-} MeshExtract_LineAdjacency_Data;
-
-static void *extract_lines_adjacency_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- /* Similar to poly_to_tri_count().
- * There is always (loop + triangle - 1) edges inside a polygon.
- * Accumulate for all polys and you get : */
- uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len;
-
- size_t vert_to_loop_size = sizeof(uint) * mr->vert_len;
-
- MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len);
- data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len);
- data->is_manifold = true;
- return data;
-}
-
-BLI_INLINE void lines_adjacency_triangle(
- uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data)
-{
- GPUIndexBufBuilder *elb = &data->elb;
- /* Iterate around the triangle's edges. */
- for (int e = 0; e < 3; e++) {
- SHIFT3(uint, v3, v2, v1);
- SHIFT3(uint, l3, l2, l1);
-
- bool inv_indices = (v2 > v3);
- void **pval;
- bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval);
- int v_data = POINTER_AS_INT(*pval);
- if (!value_is_init || v_data == NO_EDGE) {
- /* Save the winding order inside the sign bit. Because the
- * Edge-hash sort the keys and we need to compare winding later. */
- int value = (int)l1 + 1; /* 0 cannot be signed so add one. */
- *pval = POINTER_FROM_INT((inv_indices) ? -value : value);
- /* Store loop indices for remaining non-manifold edges. */
- data->vert_to_loop[v2] = l2;
- data->vert_to_loop[v3] = l3;
- }
- else {
- /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */
- *pval = POINTER_FROM_INT(NO_EDGE);
- bool inv_opposite = (v_data < 0);
- uint l_opposite = (uint)abs(v_data) - 1;
- /* TODO Make this part thread-safe. */
- if (inv_opposite == inv_indices) {
- /* Don't share edge if triangles have non matching winding. */
- GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1);
- GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite);
- data->is_manifold = false;
- }
- else {
- GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite);
- }
- }
- }
-}
-
-static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
- const struct ExtractTriBMesh_Params *params,
- void *data)
-{
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
- {
- if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
- lines_adjacency_triangle(BM_elem_index_get(elt[0]->v),
- BM_elem_index_get(elt[1]->v),
- BM_elem_index_get(elt[2]->v),
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]),
- data);
- }
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
-}
-
-static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr,
- const struct ExtractTriMesh_Params *params,
- void *data)
-{
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
- {
- const MPoly *mp = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
- mr->mloop[mlt->tri[1]].v,
- mr->mloop[mlt->tri[2]].v,
- mlt->tri[0],
- mlt->tri[1],
- mlt->tri[2],
- data);
- }
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
-}
-
-static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *cache,
- void *ibo,
- void *_data)
-{
- MeshExtract_LineAdjacency_Data *data = _data;
- /* Create edges for remaining non manifold edges. */
- EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh);
- for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
- uint v2, v3, l1, l2, l3;
- int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
- if (v_data != NO_EDGE) {
- BLI_edgehashIterator_getKey(ehi, &v2, &v3);
- l1 = (uint)abs(v_data) - 1;
- if (v_data < 0) { /* inv_opposite */
- SWAP(uint, v2, v3);
- }
- l2 = data->vert_to_loop[v2];
- l3 = data->vert_to_loop[v3];
- GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1);
- data->is_manifold = false;
- }
- }
- BLI_edgehashIterator_free(ehi);
- BLI_edgehash_free(data->eh, NULL);
-
- cache->is_manifold = data->is_manifold;
-
- GPU_indexbuf_build_in_place(&data->elb, ibo);
- MEM_freeN(data);
-}
-
-#undef NO_EDGE
-
-static const MeshExtract extract_lines_adjacency = {
- .init = extract_lines_adjacency_init,
- .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm,
- .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh,
- .finish = extract_lines_adjacency_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Triangles Indices
- * \{ */
-
-typedef struct MeshExtract_EditUvElem_Data {
- GPUIndexBufBuilder elb;
- bool sync_selection;
-} MeshExtract_EditUvElem_Data;
-
-static void *extract_edituv_tris_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len);
- data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
- return data;
-}
-
-BLI_INLINE void edituv_tri_add(
- MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3)
-{
- if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3);
- }
-}
-
-static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
- const struct ExtractTriBMesh_Params *params,
- void *data)
-{
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
- {
- edituv_tri_add(data,
- BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT),
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]));
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
-}
-
-static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr,
- const struct ExtractTriMesh_Params *params,
- void *data)
-{
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
- {
- const MPoly *mp = &mr->mpoly[mlt->poly];
- edituv_tri_add(data,
- (mp->flag & ME_HIDE) != 0,
- (mp->flag & ME_FACE_SEL) != 0,
- mlt->tri[0],
- mlt->tri[1],
- mlt->tri[2]);
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
-}
-
-static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *data)
-{
- MeshExtract_EditUvElem_Data *extract_data = data;
- GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
- MEM_freeN(extract_data);
-}
-
-static const MeshExtract extract_edituv_tris = {
- .init = extract_edituv_tris_init,
- .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm,
- .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh,
- .finish = extract_edituv_tris_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Line Indices around faces
- * \{ */
-
-static void *extract_edituv_lines_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len);
-
- data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
- return data;
-}
-
-BLI_INLINE void edituv_edge_add(
- MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2)
-{
- if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_add_line_verts(&data->elb, v1, v2);
- }
-}
-
-static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr)
- {
- edituv_edge_add(data,
- BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
- l_index,
- BM_elem_index_get(loop->next));
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop);
-}
-
-static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const int ml_index_last = mp->totloop + mp->loopstart - 1;
- const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
- const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE);
- edituv_edge_add(data,
- (mp->flag & ME_HIDE) != 0 || !real_edge,
- (mp->flag & ME_FACE_SEL) != 0,
- ml_index,
- ml_index_next);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *data)
-{
- MeshExtract_EditUvElem_Data *extract_data = data;
- GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
- MEM_freeN(extract_data);
-}
-
-static const MeshExtract extract_edituv_lines = {
- .init = extract_edituv_lines_init,
- .iter_poly_bm = extract_edituv_lines_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh,
- .finish = extract_edituv_lines_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Points Indices
- * \{ */
-
-static void *extract_edituv_points_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len);
-
- data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
- return data;
-}
-
-BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data,
- bool hidden,
- bool selected,
- int v1)
-{
- if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_add_point_vert(&data->elb, v1);
- }
-}
-
-static void extract_edituv_points_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- edituv_point_add(data,
- BM_elem_flag_test(l->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(l->f, BM_ELEM_SELECT),
- l_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
- mr->v_origindex[ml->v] != ORIGINDEX_NONE);
- edituv_point_add(
- data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *data)
-{
- MeshExtract_EditUvElem_Data *extract_data = data;
- GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
- MEM_freeN(extract_data);
-}
-
-static const MeshExtract extract_edituv_points = {
- .init = extract_edituv_points_init,
- .iter_poly_bm = extract_edituv_points_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_points_iter_poly_mesh,
- .finish = extract_edituv_points_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Facedots Indices
- * \{ */
-
-static void *extract_edituv_fdots_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
-
- data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
- return data;
-}
-
-BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data,
- bool hidden,
- bool selected,
- int face_index)
-{
- if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index);
- }
- else {
- GPU_indexbuf_set_point_restart(&data->elb, face_index);
- }
-}
-
-static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- edituv_facedot_add(
- data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), f_index);
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- if (mr->use_subsurf_fdots) {
- /* Check #ME_VERT_FACEDOT. */
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[mp_index] != ORIGINDEX_NONE);
- const bool subd_fdot = (!mr->use_subsurf_fdots ||
- (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0);
- edituv_facedot_add(data,
- ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
- (mp->flag & ME_FACE_SEL) != 0,
- mp_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[mp_index] != ORIGINDEX_NONE);
- edituv_facedot_add(data,
- ((mp->flag & ME_HIDE) != 0) || !real_fdot,
- (mp->flag & ME_FACE_SEL) != 0,
- mp_index);
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *_data)
-{
- MeshExtract_EditUvElem_Data *data = _data;
- GPU_indexbuf_build_in_place(&data->elb, ibo);
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_edituv_fdots = {
- .init = extract_edituv_fdots_init,
- .iter_poly_bm = extract_edituv_fdots_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh,
- .finish = extract_edituv_fdots_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Position and Vertex Normal
- * \{ */
-
-typedef struct PosNorLoop {
- float pos[3];
- GPUPackedNormal nor;
-} PosNorLoop;
-
-typedef struct MeshExtract_PosNor_Data {
- PosNorLoop *vbo_data;
- GPUNormal normals[];
-} MeshExtract_PosNor_Data;
-
-static void *extract_pos_nor_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* WARNING Adjust #PosNorLoop struct accordingly. */
- GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "vnor");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
-
- /* Pack normals per vert, reduce amount of computation. */
- size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len;
- MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__);
- data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo);
-
- /* Quicker than doing it for each loop. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMVert *eve;
- int v;
- BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
- data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve));
- }
- }
- else {
- const MVert *mv = mr->mvert;
- for (int v = 0; v < mr->vert_len; v++, mv++) {
- data->normals[v].low = GPU_normal_convert_i10_s3(mv->no);
- }
- }
- return data;
-}
-
-static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- PosNorLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v));
- vert->nor = data->normals[BM_elem_index_get(l->v)].low;
- BMFace *efa = l->f;
- vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- PosNorLoop *vert = &data->vbo_data[ml_index];
- const MVert *mv = &mr->mvert[ml->v];
- copy_v3_v3(vert->pos, mv->co);
- vert->nor = data->normals[ml->v].low;
- /* Flag for paint mode overlay. */
- if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
- vert->nor.w = -1;
- }
- else if (mv->flag & SELECT) {
- vert->nor.w = 1;
- }
- else {
- vert->nor.w = 0;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- int l_index = mr->loop_len + ledge_index * 2;
- PosNorLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
- copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
- vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low;
- vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low;
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- const int ml_index = mr->loop_len + ledge_index * 2;
- PosNorLoop *vert = &data->vbo_data[ml_index];
- copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
- copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
- vert[0].nor = data->normals[med->v1].low;
- vert[1].nor = data->normals[med->v2].low;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- const int l_index = offset + lvert_index;
- PosNorLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
- vert->nor = data->normals[BM_elem_index_get(eve)].low;
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
- {
- const int ml_index = offset + lvert_index;
- const int v_index = mr->lverts[lvert_index];
- PosNorLoop *vert = &data->vbo_data[ml_index];
- copy_v3_v3(vert->pos, mv->co);
- vert->nor = data->normals[v_index].low;
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(vbo),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_pos_nor = {
- .init = extract_pos_nor_init,
- .iter_poly_bm = extract_pos_nor_iter_poly_bm,
- .iter_poly_mesh = extract_pos_nor_iter_poly_mesh,
- .iter_ledge_bm = extract_pos_nor_iter_ledge_bm,
- .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh,
- .iter_lvert_bm = extract_pos_nor_iter_lvert_bm,
- .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh,
- .finish = extract_pos_nor_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Position and High Quality Vertex Normal
- * \{ */
-
-typedef struct PosNorHQLoop {
- float pos[3];
- short nor[4];
-} PosNorHQLoop;
-
-typedef struct MeshExtract_PosNorHQ_Data {
- PosNorHQLoop *vbo_data;
- GPUNormal normals[];
-} MeshExtract_PosNorHQ_Data;
-
-static void *extract_pos_nor_hq_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* WARNING Adjust #PosNorHQLoop struct accordingly. */
- GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "vnor");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
-
- /* Pack normals per vert, reduce amount of computation. */
- size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len;
- MeshExtract_PosNorHQ_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__);
- data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo);
-
- /* Quicker than doing it for each loop. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMVert *eve;
- int v;
- BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
- normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve));
- }
- }
- else {
- const MVert *mv = mr->mvert;
- for (int v = 0; v < mr->vert_len; v++, mv++) {
- copy_v3_v3_short(data->normals[v].high, mv->no);
- }
- }
- return data;
-}
-
-static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- PosNorHQLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v));
- copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l->v)].high);
-
- BMFace *efa = l->f;
- vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- PosNorHQLoop *vert = &data->vbo_data[ml_index];
- const MVert *mv = &mr->mvert[ml->v];
- copy_v3_v3(vert->pos, mv->co);
- copy_v3_v3_short(vert->nor, data->normals[ml->v].high);
-
- /* Flag for paint mode overlay. */
- if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
- vert->nor[3] = -1;
- }
- else if (mv->flag & SELECT) {
- vert->nor[3] = 1;
- }
- else {
- vert->nor[3] = 0;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- int l_index = mr->loop_len + ledge_index * 2;
- PosNorHQLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
- copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
- copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high);
- vert[0].nor[3] = 0;
- copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high);
- vert[1].nor[3] = 0;
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- const int ml_index = mr->loop_len + ledge_index * 2;
- PosNorHQLoop *vert = &data->vbo_data[ml_index];
- copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
- copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
- copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high);
- vert[0].nor[3] = 0;
- copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high);
- vert[1].nor[3] = 0;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- const int l_index = offset + lvert_index;
- PosNorHQLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
- copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high);
- vert->nor[3] = 0;
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
- {
- const int ml_index = offset + lvert_index;
- const int v_index = mr->lverts[lvert_index];
- PosNorHQLoop *vert = &data->vbo_data[ml_index];
- copy_v3_v3(vert->pos, mv->co);
- copy_v3_v3_short(vert->nor, data->normals[v_index].high);
- vert->nor[3] = 0;
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(vbo),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_pos_nor_hq = {
- .init = extract_pos_nor_hq_init,
- .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm,
- .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh,
- .iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm,
- .iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh,
- .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm,
- .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh,
- .finish = extract_pos_nor_hq_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-/* ---------------------------------------------------------------------- */
-/** \name Extract HQ Loop Normal
- * \{ */
-
-typedef struct gpuHQNor {
- short x, y, z, w;
-} gpuHQNor;
-
-static void *extract_lnor_hq_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "lnor");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- if (mr->loop_normals) {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(_l, l_index, params, mr)
- {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(_l);
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l->v));
- }
- else {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, l->f));
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
-}
-
-static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index];
- if (mr->loop_normals) {
- normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]);
- }
- else if (mp->flag & ME_SMOOTH) {
- copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no);
- }
- else {
- normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]);
- }
-
- /* Flag for paint mode overlay.
- * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
- * In paint mode it will use the un-mapped data to draw the wire-frame. */
- if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
- (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
- lnor_data->w = -1;
- }
- else if (mp->flag & ME_FACE_SEL) {
- lnor_data->w = 1;
- }
- else {
- lnor_data->w = 0;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static const MeshExtract extract_lnor_hq = {
- .init = extract_lnor_hq_init,
- .iter_poly_bm = extract_lnor_hq_iter_poly_bm,
- .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh,
- .data_flag = MR_DATA_LOOP_NOR,
- .use_threading = true,
-};
-
-/** \} */
-/* ---------------------------------------------------------------------- */
-/** \name Extract Loop Normal
- * \{ */
-
-static void *extract_lnor_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "lnor");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_lnor_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- if (mr->loop_normals) {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]);
- BMFace *efa = l->f;
- ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) {
- ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, l->v));
- }
- else {
- ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, l->f));
- }
- BMFace *efa = l->f;
- ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
-}
-
-static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index];
- if (mr->loop_normals) {
- *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]);
- }
- else if (mp->flag & ME_SMOOTH) {
- *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no);
- }
- else {
- *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]);
- }
-
- /* Flag for paint mode overlay.
- * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
- * In paint mode it will use the un-mapped data to draw the wire-frame. */
- if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
- (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
- lnor_data->w = -1;
- }
- else if (mp->flag & ME_FACE_SEL) {
- lnor_data->w = 1;
- }
- else {
- lnor_data->w = 0;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static const MeshExtract extract_lnor = {
- .init = extract_lnor_init,
- .iter_poly_bm = extract_lnor_iter_poly_bm,
- .iter_poly_mesh = extract_lnor_iter_poly_mesh,
- .data_flag = MR_DATA_LOOP_NOR,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract UV layers
- * \{ */
-
-static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
-{
- GPUVertFormat format = {0};
- GPU_vertformat_deinterleave(&format);
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- uint32_t uv_layers = cache->cd_used.uv;
- /* HACK to fix T68857 */
- if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) {
- int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
- if (layer != -1) {
- uv_layers |= (1 << layer);
- }
- }
-
- for (int i = 0; i < MAX_MTFACE; i++) {
- if (uv_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
-
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- /* UV layer name. */
- BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- /* Auto layer name. */
- BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
- GPU_vertformat_alias_add(&format, attr_name);
- /* Active render layer name. */
- if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "u");
- }
- /* Active display layer name. */
- if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "au");
- /* Alias to `pos` for edit uvs. */
- GPU_vertformat_alias_add(&format, "pos");
- }
- /* Stencil mask uv layer name. */
- if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "mu");
- }
- }
- }
-
- int v_len = mr->loop_len;
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- /* VBO will not be used, only allocate minimum of memory. */
- v_len = 1;
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, v_len);
-
- float(*uv_data)[2] = (float(*)[2])GPU_vertbuf_get_data(vbo);
- for (int i = 0; i < MAX_MTFACE; i++) {
- if (uv_layers & (1 << i)) {
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
- memcpy(uv_data, luv->uv, sizeof(*uv_data));
- uv_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) {
- memcpy(uv_data, layer_data->uv, sizeof(*uv_data));
- }
- }
- }
- }
-
- return NULL;
-}
-
-static const MeshExtract extract_uv = {
- .init = extract_uv_init,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Tangent layers
- * \{ */
-
-static void extract_tan_ex(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- GPUVertBuf *vbo,
- const bool do_hq)
-{
- GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10;
- GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT;
-
- GPUVertFormat format = {0};
- GPU_vertformat_deinterleave(&format);
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
- uint32_t tan_layers = cache->cd_used.tan;
- float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO);
- bool orco_allocated = false;
- const bool use_orco_tan = cache->cd_used.tan_orco != 0;
-
- int tan_len = 0;
- char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
-
- for (int i = 0; i < MAX_MTFACE; i++) {
- if (tan_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- /* Tangent layer name. */
- BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
- /* Active render layer name. */
- if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "t");
- }
- /* Active display layer name. */
- if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "at");
- }
-
- BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME);
- }
- }
- if (use_orco_tan && orco == NULL) {
- /* If `orco` is not available compute it ourselves */
- orco_allocated = true;
- orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMesh *bm = mr->bm;
- for (int v = 0; v < mr->vert_len; v++) {
- const BMVert *eve = BM_vert_at_index(bm, v);
- /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords.
- * not the distorted ones. */
- copy_v3_v3(orco[v], eve->co);
- }
- }
- else {
- const MVert *mv = mr->mvert;
- for (int v = 0; v < mr->vert_len; v++, mv++) {
- copy_v3_v3(orco[v], mv->co);
- }
- }
- BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0);
- }
-
- /* Start Fresh */
- CustomData loop_data;
- CustomData_reset(&loop_data);
- if (tan_len != 0 || use_orco_tan) {
- short tangent_mask = 0;
- bool calc_active_tangent = false;
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BKE_editmesh_loop_tangent_calc(mr->edit_bmesh,
- calc_active_tangent,
- tangent_names,
- tan_len,
- mr->poly_normals,
- mr->loop_normals,
- orco,
- &loop_data,
- mr->loop_len,
- &tangent_mask);
- }
- else {
- BKE_mesh_calc_loop_tangent_ex(mr->mvert,
- mr->mpoly,
- mr->poly_len,
- mr->mloop,
- mr->mlooptri,
- mr->tri_len,
- cd_ldata,
- calc_active_tangent,
- tangent_names,
- tan_len,
- mr->poly_normals,
- mr->loop_normals,
- orco,
- &loop_data,
- mr->loop_len,
- &tangent_mask);
- }
- }
-
- if (use_orco_tan) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
- GPU_vertformat_alias_add(&format, "t");
- GPU_vertformat_alias_add(&format, "at");
- }
-
- if (orco_allocated) {
- MEM_SAFE_FREE(orco);
- }
-
- int v_len = mr->loop_len;
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- /* VBO will not be used, only allocate minimum of memory. */
- v_len = 1;
- }
-
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, v_len);
-
- if (do_hq) {
- short(*tan_data)[4] = (short(*)[4])GPU_vertbuf_get_data(vbo);
- for (int i = 0; i < tan_len; i++) {
- const char *name = tangent_names[i];
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(
- &loop_data, CD_TANGENT, name);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
- normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
- (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
- tan_data++;
- }
- }
- if (use_orco_tan) {
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
- normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
- (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
- tan_data++;
- }
- }
- }
- else {
- GPUPackedNormal *tan_data = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
- for (int i = 0; i < tan_len; i++) {
- const char *name = tangent_names[i];
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(
- &loop_data, CD_TANGENT, name);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
- *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
- tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
- tan_data++;
- }
- }
- if (use_orco_tan) {
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
- *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
- tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
- tan_data++;
- }
- }
- }
-
- CustomData_free(&loop_data, mr->loop_len);
-}
-
-static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
-{
- extract_tan_ex(mr, cache, buf, false);
- return NULL;
-}
-
-static const MeshExtract extract_tan = {
- .init = extract_tan_init,
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract HQ Tangent layers
- * \{ */
-
-static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
-{
- extract_tan_ex(mr, cache, buf, true);
- return NULL;
-}
-
-static const MeshExtract extract_tan_hq = {
- .init = extract_tan_hq_init,
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Sculpt Data
- * \{ */
-
-static void *extract_sculpt_data_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- GPUVertFormat format = {0};
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
- CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata;
-
- float *cd_mask = CustomData_get_layer(cd_vdata, CD_PAINT_MASK);
- int *cd_face_set = CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS);
-
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "fset", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- typedef struct gpuSculptData {
- uint8_t face_set_color[4];
- float mask;
- } gpuSculptData;
-
- gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo);
- MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK);
- int cd_face_set_ofs = CustomData_get_offset(cd_pdata, CD_SCULPT_FACE_SETS);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- float v_mask = 0.0f;
- if (cd_mask) {
- v_mask = BM_ELEM_CD_GET_FLOAT(l_iter->v, cd_mask_ofs);
- }
- vbo_data->mask = v_mask;
- uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
- if (cd_face_set) {
- const int face_set_id = BM_ELEM_CD_GET_INT(l_iter->f, cd_face_set_ofs);
- if (face_set_id != mr->me->face_sets_color_default) {
- BKE_paint_face_set_overlay_color_get(
- face_set_id, mr->me->face_sets_color_seed, face_set_color);
- }
- }
- copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color);
- vbo_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- int mp_loop = 0;
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
- const MPoly *p = &mr->mpoly[mp_index];
- for (int l = 0; l < p->totloop; l++) {
- float v_mask = 0.0f;
- if (cd_mask) {
- v_mask = cd_mask[loops[mp_loop].v];
- }
- vbo_data->mask = v_mask;
-
- uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
- if (cd_face_set) {
- const int face_set_id = cd_face_set[mp_index];
- /* Skip for the default color Face Set to render it white. */
- if (face_set_id != mr->me->face_sets_color_default) {
- BKE_paint_face_set_overlay_color_get(
- face_set_id, mr->me->face_sets_color_seed, face_set_color);
- }
- }
- copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color);
- mp_loop++;
- vbo_data++;
- }
- }
- }
-
- return NULL;
-}
-
-static const MeshExtract extract_sculpt_data = {
- .init = extract_sculpt_data_init,
- .data_flag = 0,
- /* TODO: enable threading. */
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract VCol
- * \{ */
-
-static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
-{
- GPUVertFormat format = {0};
- GPU_vertformat_deinterleave(&format);
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
- uint32_t vcol_layers = cache->cd_used.vcol;
- uint32_t svcol_layers = cache->cd_used.sculpt_vcol;
-
- for (int i = 0; i < MAX_MCOL; i++) {
- if (vcol_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
-
- BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-
- if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) {
- GPU_vertformat_alias_add(&format, "c");
- }
- if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) {
- GPU_vertformat_alias_add(&format, "ac");
- }
-
- /* Gather number of auto layers. */
- /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */
- if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 &&
- CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) {
- BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
- GPU_vertformat_alias_add(&format, attr_name);
- }
- }
- }
-
- /* Sculpt Vertex Colors */
- if (U.experimental.use_sculpt_vertex_colors) {
- for (int i = 0; i < 8; i++) {
- if (svcol_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
-
- BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-
- if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) {
- GPU_vertformat_alias_add(&format, "c");
- }
- if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) {
- GPU_vertformat_alias_add(&format, "ac");
- }
- /* Gather number of auto layers. */
- /* We only do `vcols` that are not overridden by `uvs`. */
- if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
- BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
- GPU_vertformat_alias_add(&format, attr_name);
- }
- }
- }
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- typedef struct gpuMeshVcol {
- ushort r, g, b, a;
- } gpuMeshVcol;
-
- gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo);
- MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
-
- for (int i = 0; i < MAX_MCOL; i++) {
- if (vcol_layers & (1 << i)) {
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
- vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
- vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
- vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
- vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
- vcol_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) {
- vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
- vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
- vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
- vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
- }
- }
- }
-
- if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) {
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs);
- vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]);
- vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]);
- vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]);
- vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]);
- vcol_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) {
- vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]);
- vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]);
- vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]);
- vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]);
- }
- }
- }
- }
- return NULL;
-}
-
-static const MeshExtract extract_vcol = {
- .init = extract_vcol_init,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Orco
- * \{ */
-
-typedef struct MeshExtract_Orco_Data {
- float (*vbo_data)[4];
- float (*orco)[3];
-} MeshExtract_Orco_Data;
-
-static void *extract_orco_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
- * attributes. This is a substantial waste of video-ram and should be done another way.
- * Unfortunately, at the time of writing, I did not found any other "non disruptive"
- * alternative. */
- GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- CustomData *cd_vdata = &mr->me->vdata;
-
- MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__);
- data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo);
- data->orco = CustomData_get_layer(cd_vdata, CD_ORCO);
- /* Make sure `orco` layer was requested only if needed! */
- BLI_assert(data->orco);
- return data;
-}
-
-static void extract_orco_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr)
- {
- float *loop_orco = orco_data->vbo_data[l_index];
- copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]);
- loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop);
-}
-
-static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
- float *loop_orco = orco_data->vbo_data[ml_index];
- copy_v3_v3(loop_orco, orco_data->orco[ml->v]);
- loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_orco_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_orco = {
- .init = extract_orco_init,
- .iter_poly_bm = extract_orco_iter_poly_bm,
- .iter_poly_mesh = extract_orco_iter_poly_mesh,
- .finish = extract_orco_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edge Factor
- * Defines how much an edge is visible.
- * \{ */
-
-typedef struct MeshExtract_EdgeFac_Data {
- uchar *vbo_data;
- bool use_edge_render;
- /* Number of loop per edge. */
- uchar edge_loop_count[0];
-} MeshExtract_EdgeFac_Data;
-
-static float loop_edge_factor_get(const float f_no[3],
- const float v_co[3],
- const float v_no[3],
- const float v_next_co[3])
-{
- float enor[3], evec[3];
- sub_v3_v3v3(evec, v_next_co, v_co);
- cross_v3_v3v3(enor, v_no, evec);
- normalize_v3(enor);
- float d = fabsf(dot_v3v3(enor, f_no));
- /* Re-scale to the slider range. */
- d *= (1.0f / 0.065f);
- CLAMP(d, 0.0f, 1.0f);
- return d;
-}
-
-static void *extract_edge_fac_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
-
- MeshExtract_EdgeFac_Data *data;
-
- if (mr->extract_type == MR_EXTRACT_MESH) {
- size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len;
- data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__);
-
- /* HACK(fclem) Detecting the need for edge render.
- * We could have a flag in the mesh instead or check the modifier stack. */
- const MEdge *med = mr->medge;
- for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) {
- if ((med->flag & ME_EDGERENDER) == 0) {
- data->use_edge_render = true;
- break;
- }
- }
- }
- else {
- data = MEM_callocN(sizeof(*data), __func__);
- /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */
- data->use_edge_render = true;
- }
-
- data->vbo_data = GPU_vertbuf_get_data(vbo);
- return data;
-}
-
-static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- if (BM_edge_is_manifold(l->e)) {
- float ratio = loop_edge_factor_get(bm_face_no_get(mr, l->f),
- bm_vert_co_get(mr, l->v),
- bm_vert_no_get(mr, l->v),
- bm_vert_co_get(mr, l->next->v));
- data->vbo_data[l_index] = ratio * 253 + 1;
- }
- else {
- data->vbo_data[l_index] = 255;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
-
- if (data->use_edge_render) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MEdge *med = &mr->medge[ml->e];
- data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- /* Count loop per edge to detect non-manifold. */
- if (data->edge_loop_count[ml->e] < 3) {
- data->edge_loop_count[ml->e]++;
- }
- if (data->edge_loop_count[ml->e] == 2) {
- /* Manifold */
- const int ml_index_last = mp->totloop + mp->loopstart - 1;
- const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
- const MLoop *ml_next = &mr->mloop[ml_index_other];
- const MVert *v1 = &mr->mvert[ml->v];
- const MVert *v2 = &mr->mvert[ml_next->v];
- float vnor_f[3];
- normal_short_to_float_v3(vnor_f, v1->no);
- float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co);
- data->vbo_data[ml_index] = ratio * 253 + 1;
- }
- else {
- /* Non-manifold */
- data->vbo_data[ml_index] = 255;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
-}
-
-static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255;
- data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255;
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255;
- data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_edge_fac_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = _data;
-
- if (GPU_crappy_amd_driver()) {
- GPUVertBuf *vbo = (GPUVertBuf *)buf;
- /* Some AMD drivers strangely crash with VBO's with a one byte format.
- * To workaround we reinitialize the VBO with another format and convert
- * all bytes to floats. */
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
- /* We keep the data reference in data->vbo_data. */
- data->vbo_data = GPU_vertbuf_steal_data(vbo);
- GPU_vertbuf_clear(vbo);
-
- int buf_len = mr->loop_len + mr->loop_loose_len;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, buf_len);
-
- float *fdata = (float *)GPU_vertbuf_get_data(vbo);
- for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) {
- *fdata = data->vbo_data[ml_index] / 255.0f;
- }
- /* Free old byte data. */
- MEM_freeN(data->vbo_data);
- }
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_edge_fac = {
- .init = extract_edge_fac_init,
- .iter_poly_bm = extract_edge_fac_iter_poly_bm,
- .iter_poly_mesh = extract_edge_fac_iter_poly_mesh,
- .iter_ledge_bm = extract_edge_fac_iter_ledge_bm,
- .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh,
- .finish = extract_edge_fac_finish,
- .data_flag = MR_DATA_POLY_NOR,
- .use_threading = false,
-};
-
-/** \} */
-/* ---------------------------------------------------------------------- */
-/** \name Extract Vertex Weight
- * \{ */
-
-typedef struct MeshExtract_Weight_Data {
- float *vbo_data;
- const DRW_MeshWeightState *wstate;
- const MDeformVert *dvert; /* For #Mesh. */
- int cd_ofs; /* For #BMesh. */
-} MeshExtract_Weight_Data;
-
-static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate)
-{
- /* Error state. */
- if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) {
- return -2.0f;
- }
- if (dvert == NULL) {
- return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f;
- }
-
- float input = 0.0f;
- if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
- /* Multi-Paint feature */
- bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE |
- DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE));
- input = BKE_defvert_multipaint_collective_weight(dvert,
- wstate->defgroup_len,
- wstate->defgroup_sel,
- wstate->defgroup_sel_count,
- is_normalized);
- /* make it black if the selected groups have no weight on a vertex */
- if (input == 0.0f) {
- return -1.0f;
- }
- }
- else {
- /* default, non tricky behavior */
- input = BKE_defvert_find_weight(dvert, wstate->defgroup_active);
-
- if (input == 0.0f) {
- switch (wstate->alert_mode) {
- case OB_DRAW_GROUPUSER_ACTIVE:
- return -1.0f;
- break;
- case OB_DRAW_GROUPUSER_ALL:
- if (BKE_defvert_is_weight_zero(dvert, wstate->defgroup_len)) {
- return -1.0f;
- }
- break;
- }
- }
- }
-
- /* Lock-Relative: display the fraction of current weight vs total unlocked weight. */
- if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) {
- input = BKE_defvert_lock_relative_weight(
- input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked);
- }
-
- CLAMP(input, 0.0f, 1.0f);
- return input;
-}
-
-static void *extract_weights_init(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
-
- MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (float *)GPU_vertbuf_get_data(vbo);
- data->wstate = &cache->weight_state;
-
- if (data->wstate->defgroup_active == -1) {
- /* Nothing to show. */
- data->dvert = NULL;
- data->cd_ofs = -1;
- }
- else if (mr->extract_type == MR_EXTRACT_BMESH) {
- data->dvert = NULL;
- data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT);
- }
- else {
- data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT);
- data->cd_ofs = -1;
- }
- return data;
-}
-
-static void extract_weights_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_Weight_Data *data = _data;
- if (data->cd_ofs != -1) {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l->v, data->cd_ofs);
- data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
-}
-
-static void extract_weights_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_Weight_Data *data = _data;
- if (data->dvert != NULL) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MDeformVert *dvert = &data->dvert[ml->v];
- data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- const MDeformVert *dvert = NULL;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
-}
-
-static void extract_weights_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_weights = {
- .init = extract_weights_init,
- .iter_poly_bm = extract_weights_iter_poly_bm,
- .iter_poly_mesh = extract_weights_iter_poly_mesh,
- .finish = extract_weights_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit Mode Data / Flags
- * \{ */
-
-typedef struct EditLoopData {
- uchar v_flag;
- uchar e_flag;
- uchar crease;
- uchar bweight;
-} EditLoopData;
-
-static void mesh_render_data_face_flag(const MeshRenderData *mr,
- BMFace *efa,
- const int cd_ofs,
- EditLoopData *eattr)
-{
- if (efa == mr->efa_act) {
- eattr->v_flag |= VFLAG_FACE_ACTIVE;
- }
- if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- eattr->v_flag |= VFLAG_FACE_SELECTED;
- }
-
- if (efa == mr->efa_act_uv) {
- eattr->v_flag |= VFLAG_FACE_UV_ACTIVE;
- }
- if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) {
- eattr->v_flag |= VFLAG_FACE_UV_SELECT;
- }
-
-#ifdef WITH_FREESTYLE
- if (mr->freestyle_face_ofs != -1) {
- const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs);
- if (ffa->flag & FREESTYLE_FACE_MARK) {
- eattr->v_flag |= VFLAG_FACE_FREESTYLE;
- }
- }
-#endif
-}
-
-static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr)
-{
- const ToolSettings *ts = mr->toolsettings;
- const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0;
- const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE);
-
- if (eed == mr->eed_act) {
- eattr->e_flag |= VFLAG_EDGE_ACTIVE;
- }
- if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
- eattr->e_flag |= VFLAG_EDGE_SELECTED;
- }
- if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) &&
- BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) {
- eattr->e_flag |= VFLAG_EDGE_SELECTED;
- eattr->e_flag |= VFLAG_VERT_SELECTED;
- }
- if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
- eattr->e_flag |= VFLAG_EDGE_SEAM;
- }
- if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) {
- eattr->e_flag |= VFLAG_EDGE_SHARP;
- }
-
- /* Use active edge color for active face edges because
- * specular highlights make it hard to see T55456#510873.
- *
- * This isn't ideal since it can't be used when mixing edge/face modes
- * but it's still better than not being able to see the active face. */
- if (is_face_only_select_mode) {
- if (mr->efa_act != NULL) {
- if (BM_edge_in_face(eed, mr->efa_act)) {
- eattr->e_flag |= VFLAG_EDGE_ACTIVE;
- }
- }
- }
-
- /* Use a byte for value range */
- if (mr->crease_ofs != -1) {
- float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs);
- if (crease > 0) {
- eattr->crease = (uchar)(crease * 255.0f);
- }
- }
- /* Use a byte for value range */
- if (mr->bweight_ofs != -1) {
- float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs);
- if (bweight > 0) {
- eattr->bweight = (uchar)(bweight * 255.0f);
- }
- }
-#ifdef WITH_FREESTYLE
- if (mr->freestyle_edge_ofs != -1) {
- const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs);
- if (fed->flag & FREESTYLE_EDGE_MARK) {
- eattr->e_flag |= VFLAG_EDGE_FREESTYLE;
- }
- }
-#endif
-}
-
-static void mesh_render_data_loop_flag(const MeshRenderData *mr,
- BMLoop *l,
- const int cd_ofs,
- EditLoopData *eattr)
-{
- if (cd_ofs == -1) {
- return;
- }
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs);
- if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) {
- eattr->v_flag |= VFLAG_VERT_UV_PINNED;
- }
- if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) {
- eattr->v_flag |= VFLAG_VERT_UV_SELECT;
- }
-}
-
-static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr,
- BMLoop *l,
- const int cd_ofs,
- EditLoopData *eattr)
-{
- if (cd_ofs == -1) {
- return;
- }
- if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) {
- eattr->v_flag |= VFLAG_EDGE_UV_SELECT;
- eattr->v_flag |= VFLAG_VERT_UV_SELECT;
- }
-}
-
-static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr)
-{
- if (eve == mr->eve_act) {
- eattr->e_flag |= VFLAG_VERT_ACTIVE;
- }
- if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- eattr->e_flag |= VFLAG_VERT_SELECTED;
- }
-}
-
-static void *extract_edit_data_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* WARNING: Adjust #EditLoopData struct accordingly. */
- GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
- GPU_vertformat_alias_add(&format, "flag");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
-
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- EditLoopData *data = (EditLoopData *)_data + l_index;
- memset(data, 0x0, sizeof(*data));
- mesh_render_data_face_flag(mr, l->f, -1, data);
- mesh_render_data_edge_flag(mr, l->e, data);
- mesh_render_data_vert_flag(mr, l->v, data);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- EditLoopData *data = (EditLoopData *)_data + ml_index;
- memset(data, 0x0, sizeof(*data));
- BMFace *efa = bm_original_face_get(mr, mp_index);
- BMEdge *eed = bm_original_edge_get(mr, ml->e);
- BMVert *eve = bm_original_vert_get(mr, ml->v);
- if (efa) {
- mesh_render_data_face_flag(mr, efa, -1, data);
- }
- if (eed) {
- mesh_render_data_edge_flag(mr, eed, data);
- }
- if (eve) {
- mesh_render_data_vert_flag(mr, eve, data);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *_data)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2);
- memset(data, 0x0, sizeof(*data) * 2);
- mesh_render_data_edge_flag(mr, eed, &data[0]);
- data[1] = data[0];
- mesh_render_data_vert_flag(mr, eed->v1, &data[0]);
- mesh_render_data_vert_flag(mr, eed->v2, &data[1]);
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *_data)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2;
- memset(data, 0x0, sizeof(*data) * 2);
- const int e_index = mr->ledges[ledge_index];
- BMEdge *eed = bm_original_edge_get(mr, e_index);
- BMVert *eve1 = bm_original_vert_get(mr, med->v1);
- BMVert *eve2 = bm_original_vert_get(mr, med->v2);
- if (eed) {
- mesh_render_data_edge_flag(mr, eed, &data[0]);
- data[1] = data[0];
- }
- if (eve1) {
- mesh_render_data_vert_flag(mr, eve1, &data[0]);
- }
- if (eve2) {
- mesh_render_data_vert_flag(mr, eve2, &data[1]);
- }
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *_data)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- EditLoopData *data = (EditLoopData *)_data + offset + lvert_index;
- memset(data, 0x0, sizeof(*data));
- mesh_render_data_vert_flag(mr, eve, data);
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *_data)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
- {
- EditLoopData *data = (EditLoopData *)_data + offset + lvert_index;
- memset(data, 0x0, sizeof(*data));
- const int v_index = mr->lverts[lvert_index];
- BMVert *eve = bm_original_vert_get(mr, v_index);
- if (eve) {
- mesh_render_data_vert_flag(mr, eve, data);
- }
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static const MeshExtract extract_edit_data = {
- .init = extract_edit_data_init,
- .iter_poly_bm = extract_edit_data_iter_poly_bm,
- .iter_poly_mesh = extract_edit_data_iter_poly_mesh,
- .iter_ledge_bm = extract_edit_data_iter_ledge_bm,
- .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh,
- .iter_lvert_bm = extract_edit_data_iter_lvert_bm,
- .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Data / Flags
- * \{ */
-
-typedef struct MeshExtract_EditUVData_Data {
- EditLoopData *vbo_data;
- int cd_ofs;
-} MeshExtract_EditUVData_Data;
-
-static void *extract_edituv_data_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* WARNING: Adjust #EditLoopData struct accordingly. */
- GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
- GPU_vertformat_alias_add(&format, "flag");
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
-
- MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
- data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
- return data;
-}
-
-static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- MeshExtract_EditUVData_Data *data = _data;
- EditLoopData *eldata = &data->vbo_data[l_index];
- memset(eldata, 0x0, sizeof(*eldata));
- mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata);
- mesh_render_data_face_flag(mr, l->f, data->cd_ofs, eldata);
- mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_EditUVData_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- EditLoopData *eldata = &data->vbo_data[ml_index];
- memset(eldata, 0x0, sizeof(*eldata));
- BMFace *efa = bm_original_face_get(mr, mp_index);
- if (efa) {
- BMEdge *eed = bm_original_edge_get(mr, ml->e);
- BMVert *eve = bm_original_vert_get(mr, ml->v);
- if (eed && eve) {
- /* Loop on an edge endpoint. */
- BMLoop *l = BM_face_edge_share_loop(efa, eed);
- mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata);
- mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
- }
- else {
- if (eed == NULL) {
- /* Find if the loop's vert is not part of an edit edge.
- * For this, we check if the previous loop was on an edge. */
- const int ml_index_last = mp->loopstart + mp->totloop - 1;
- const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1);
- const MLoop *ml_prev = &mr->mloop[l_prev];
- eed = bm_original_edge_get(mr, ml_prev->e);
- }
- if (eed) {
- /* Mapped points on an edge between two edit verts. */
- BMLoop *l = BM_face_edge_share_loop(efa, eed);
- mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
- }
- }
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_edituv_data = {
- .init = extract_edituv_data_init,
- .iter_poly_bm = extract_edituv_data_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_data_iter_poly_mesh,
- .finish = extract_edituv_data_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV area stretch
- * \{ */
-
-static void *extract_edituv_stretch_area_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- return NULL;
-}
-
-BLI_INLINE float area_ratio_get(float area, float uvarea)
-{
- if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) {
- /* Tag inversion by using the sign. */
- return (area > uvarea) ? (uvarea / area) : -(area / uvarea);
- }
- return 0.0f;
-}
-
-BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio)
-{
- ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio;
- return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
-}
-
-static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *buf,
- void *UNUSED(data))
-{
- float tot_area = 0.0f, tot_uv_area = 0.0f;
- float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- CustomData *cd_ldata = &mr->bm->ldata;
- int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
-
- BMFace *efa;
- BMIter f_iter;
- int f;
- BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
- float area = BM_face_calc_area(efa);
- float uvarea = BM_face_calc_area_uv(efa, uv_ofs);
- tot_area += area;
- tot_uv_area += uvarea;
- area_ratio[f] = area_ratio_get(area, uvarea);
- }
- }
- else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
- const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert);
- float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data);
- tot_area += area;
- tot_uv_area += uvarea;
- area_ratio[mp_index] = area_ratio_get(area, uvarea);
- }
- }
-
- cache->tot_area = tot_area;
- cache->tot_uv_area = tot_uv_area;
-
- /* Convert in place to avoid an extra allocation */
- uint16_t *poly_stretch = (uint16_t *)area_ratio;
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
- poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX;
- }
-
- /* Copy face data for each loop. */
- GPUVertBuf *vbo = buf;
- uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMFace *efa;
- BMIter f_iter;
- int f, l_index = 0;
- BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
- for (int i = 0; i < efa->len; i++, l_index++) {
- loop_stretch[l_index] = poly_stretch[f];
- }
- }
- }
- else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- for (int i = 0; i < mp->totloop; i++, l_index++) {
- loop_stretch[l_index] = poly_stretch[mp_index];
- }
- }
- }
-
- MEM_freeN(area_ratio);
-}
-
-static const MeshExtract extract_edituv_stretch_area = {
- .init = extract_edituv_stretch_area_init,
- .finish = mesh_edituv_stretch_area_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV angle stretch
- * \{ */
-
-typedef struct UVStretchAngle {
- int16_t angle;
- int16_t uv_angles[2];
-} UVStretchAngle;
-
-typedef struct MeshExtract_StretchAngle_Data {
- UVStretchAngle *vbo_data;
- MLoopUV *luv;
- float auv[2][2], last_auv[2];
- float av[2][3], last_av[3];
- int cd_ofs;
-} MeshExtract_StretchAngle_Data;
-
-static void compute_normalize_edge_vectors(float auv[2][2],
- float av[2][3],
- const float uv[2],
- const float uv_prev[2],
- const float co[3],
- const float co_prev[3])
-{
- /* Move previous edge. */
- copy_v2_v2(auv[0], auv[1]);
- copy_v3_v3(av[0], av[1]);
- /* 2d edge */
- sub_v2_v2v2(auv[1], uv_prev, uv);
- normalize_v2(auv[1]);
- /* 3d edge */
- sub_v3_v3v3(av[1], co_prev, co);
- normalize_v3(av[1]);
-}
-
-static short v2_to_short_angle(const float v[2])
-{
- return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX;
-}
-
-static void edituv_get_edituv_stretch_angle(float auv[2][2],
- const float av[2][3],
- UVStretchAngle *r_stretch)
-{
- /* Send UV's to the shader and let it compute the aspect corrected angle. */
- r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]);
- r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]);
- /* Compute 3D angle here. */
- r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX;
-
-#if 0 /* here for reference, this is done in shader now. */
- float uvang = angle_normalized_v2v2(auv0, auv1);
- float ang = angle_normalized_v3v3(av0, av1);
- float stretch = fabsf(uvang - ang) / (float)M_PI;
- return 1.0f - pow2f(1.0f - stretch);
-#endif
-}
-
-static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* Waning: adjust #UVStretchAngle struct accordingly. */
- GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo);
-
- /* Special iterator needed to save about half of the computing cost. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
- }
- else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
- data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
- }
- return data;
-}
-
-static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_StretchAngle_Data *data = _data;
- float(*auv)[2] = data->auv, *last_auv = data->last_auv;
- float(*av)[3] = data->av, *last_av = data->last_av;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- const MLoopUV *luv, *luv_next;
- BMLoop *l_next = l->next;
- BMFace *efa = l->f;
- if (l == BM_FACE_FIRST_LOOP(efa)) {
- /* First loop in face. */
- BMLoop *l_tmp = l->prev;
- BMLoop *l_next_tmp = l;
- luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs);
- luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs);
- compute_normalize_edge_vectors(auv,
- av,
- luv->uv,
- luv_next->uv,
- bm_vert_co_get(mr, l_tmp->v),
- bm_vert_co_get(mr, l_next_tmp->v));
- /* Save last edge. */
- copy_v2_v2(last_auv, auv[1]);
- copy_v3_v3(last_av, av[1]);
- }
- if (l_next == BM_FACE_FIRST_LOOP(efa)) {
- /* Move previous edge. */
- copy_v2_v2(auv[0], auv[1]);
- copy_v3_v3(av[0], av[1]);
- /* Copy already calculated last edge. */
- copy_v2_v2(auv[1], last_auv);
- copy_v3_v3(av[1], last_av);
- }
- else {
- luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs);
- luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs);
- compute_normalize_edge_vectors(
- auv, av, luv->uv, luv_next->uv, bm_vert_co_get(mr, l->v), bm_vert_co_get(mr, l_next->v));
- }
- edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_StretchAngle_Data *data = _data;
-
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- float(*auv)[2] = data->auv, *last_auv = data->last_auv;
- float(*av)[3] = data->av, *last_av = data->last_av;
- int l_next = ml_index + 1, ml_index_end = mp->loopstart + mp->totloop;
- const MVert *v, *v_next;
- if (ml_index == mp->loopstart) {
- /* First loop in face. */
- const int ml_index_last = ml_index_end - 1;
- const int l_next_tmp = mp->loopstart;
- v = &mr->mvert[mr->mloop[ml_index_last].v];
- v_next = &mr->mvert[mr->mloop[l_next_tmp].v];
- compute_normalize_edge_vectors(
- auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co);
- /* Save last edge. */
- copy_v2_v2(last_auv, auv[1]);
- copy_v3_v3(last_av, av[1]);
- }
- if (l_next == ml_index_end) {
- l_next = mp->loopstart;
- /* Move previous edge. */
- copy_v2_v2(auv[0], auv[1]);
- copy_v3_v3(av[0], av[1]);
- /* Copy already calculated last edge. */
- copy_v2_v2(auv[1], last_auv);
- copy_v3_v3(av[1], last_av);
- }
- else {
- v = &mr->mvert[mr->mloop[ml_index].v];
- v_next = &mr->mvert[mr->mloop[l_next].v];
- compute_normalize_edge_vectors(
- auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co);
- }
- edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_edituv_stretch_angle = {
- .init = extract_edituv_stretch_angle_init,
- .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh,
- .finish = extract_edituv_stretch_angle_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit Mesh Analysis Colors
- * \{ */
-
-static void *extract_mesh_analysis_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- return NULL;
-}
-
-static void axis_from_enum_v3(float v[3], const char axis)
-{
- zero_v3(v);
- if (axis < 3) {
- v[axis] = 1.0f;
- }
- else {
- v[axis - 3] = -1.0f;
- }
-}
-
-BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange)
-{
- if (fac < min) {
- fac = 1.0f;
- }
- else if (fac > max) {
- fac = -1.0f;
- }
- else {
- fac = (fac - min) * minmax_irange;
- fac = 1.0f - fac;
- CLAMP(fac, 0.0f, 1.0f);
- }
- return fac;
-}
-
-static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang)
-{
- const MeshStatVis *statvis = &mr->toolsettings->statvis;
- const float min = statvis->overhang_min / (float)M_PI;
- const float max = statvis->overhang_max / (float)M_PI;
- const char axis = statvis->overhang_axis;
- BMEditMesh *em = mr->edit_bmesh;
- BMIter iter;
- BMesh *bm = em->bm;
- BMFace *f;
- float dir[3];
- const float minmax_irange = 1.0f / (max - min);
-
- BLI_assert(min <= max);
-
- axis_from_enum_v3(dir, axis);
-
- /* now convert into global space */
- mul_transposed_mat3_m4_v3(mr->obmat, dir);
- normalize_v3(dir);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int l_index = 0;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI;
- fac = overhang_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l_index++) {
- r_overhang[l_index] = fac;
- }
- }
- }
- else {
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI;
- fac = overhang_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mp->totloop; i++, l_index++) {
- r_overhang[l_index] = fac;
- }
- }
- }
-}
-
-/**
- * Needed so we can use jitter values for face interpolation.
- */
-static void uv_from_jitter_v2(float uv[2])
-{
- uv[0] += 0.5f;
- uv[1] += 0.5f;
- if (uv[0] + uv[1] > 1.0f) {
- uv[0] = 1.0f - uv[0];
- uv[1] = 1.0f - uv[1];
- }
-
- clamp_v2(uv, 0.0f, 1.0f);
-}
-
-BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange)
-{
- /* important not '<=' */
- if (fac < max) {
- fac = (fac - min) * minmax_irange;
- fac = 1.0f - fac;
- CLAMP(fac, 0.0f, 1.0f);
- }
- else {
- fac = -1.0f;
- }
- return fac;
-}
-
-static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness)
-{
- const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */
- /* cheating to avoid another allocation */
- float *face_dists = r_thickness + (mr->loop_len - mr->poly_len);
- BMEditMesh *em = mr->edit_bmesh;
- const float scale = 1.0f / mat4_to_scale(mr->obmat);
- const MeshStatVis *statvis = &mr->toolsettings->statvis;
- const float min = statvis->thickness_min * scale;
- const float max = statvis->thickness_max * scale;
- const float minmax_irange = 1.0f / (max - min);
- const int samples = statvis->thickness_samples;
- float jit_ofs[32][2];
- BLI_assert(samples <= 32);
- BLI_assert(min <= max);
-
- copy_vn_fl(face_dists, mr->poly_len, max);
-
- BLI_jitter_init(jit_ofs, samples);
- for (int j = 0; j < samples; j++) {
- uv_from_jitter_v2(jit_ofs[j]);
- }
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMesh *bm = em->bm;
- BM_mesh_elem_index_ensure(bm, BM_FACE);
-
- struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
- struct BMLoop *(*looptris)[3] = em->looptris;
- for (int i = 0; i < mr->tri_len; i++) {
- BMLoop **ltri = looptris[i];
- const int index = BM_elem_index_get(ltri[0]->f);
- const float *cos[3] = {
- bm_vert_co_get(mr, ltri[0]->v),
- bm_vert_co_get(mr, ltri[1]->v),
- bm_vert_co_get(mr, ltri[2]->v),
- };
- float ray_co[3];
- float ray_no[3];
-
- normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
-
- for (int j = 0; j < samples; j++) {
- float dist = face_dists[index];
- interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
- madd_v3_v3fl(ray_co, ray_no, eps_offset);
-
- BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL);
- if (f_hit && dist < face_dists[index]) {
- float angle_fac = fabsf(
- dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit)));
- angle_fac = 1.0f - angle_fac;
- angle_fac = angle_fac * angle_fac * angle_fac;
- angle_fac = 1.0f - angle_fac;
- dist /= angle_fac;
- if (dist < face_dists[index]) {
- face_dists[index] = dist;
- }
- }
- }
- }
- BKE_bmbvh_free(bmtree);
-
- BMIter iter;
- BMFace *f;
- int l_index = 0;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- float fac = face_dists[BM_elem_index_get(f)];
- fac = thickness_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l_index++) {
- r_thickness[l_index] = fac;
- }
- }
- }
- else {
- BVHTreeFromMesh treeData = {NULL};
-
- BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
- const MLoopTri *mlooptri = mr->mlooptri;
- for (int i = 0; i < mr->tri_len; i++, mlooptri++) {
- const int index = mlooptri->poly;
- const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co,
- mr->mvert[mr->mloop[mlooptri->tri[1]].v].co,
- mr->mvert[mr->mloop[mlooptri->tri[2]].v].co};
- float ray_co[3];
- float ray_no[3];
-
- normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
-
- for (int j = 0; j < samples; j++) {
- interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
- madd_v3_v3fl(ray_co, ray_no, eps_offset);
-
- BVHTreeRayHit hit;
- hit.index = -1;
- hit.dist = face_dists[index];
- if ((BLI_bvhtree_ray_cast(
- tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) &&
- hit.dist < face_dists[index]) {
- float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no));
- angle_fac = 1.0f - angle_fac;
- angle_fac = angle_fac * angle_fac * angle_fac;
- angle_fac = 1.0f - angle_fac;
- hit.dist /= angle_fac;
- if (hit.dist < face_dists[index]) {
- face_dists[index] = hit.dist;
- }
- }
- }
- }
-
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- float fac = face_dists[mp_index];
- fac = thickness_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mp->totloop; i++, l_index++) {
- r_thickness[l_index] = fac;
- }
- }
- }
-}
-
-struct BVHTree_OverlapData {
- const Mesh *me;
- const MLoopTri *mlooptri;
- float epsilon;
-};
-
-static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
-{
- struct BVHTree_OverlapData *data = userdata;
- const Mesh *me = data->me;
-
- const MLoopTri *tri_a = &data->mlooptri[index_a];
- const MLoopTri *tri_b = &data->mlooptri[index_b];
-
- if (UNLIKELY(tri_a->poly == tri_b->poly)) {
- return false;
- }
-
- const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co,
- me->mvert[me->mloop[tri_a->tri[1]].v].co,
- me->mvert[me->mloop[tri_a->tri[2]].v].co};
- const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co,
- me->mvert[me->mloop[tri_b->tri[1]].v].co,
- me->mvert[me->mloop[tri_b->tri[2]].v].co};
- float ix_pair[2][3];
- int verts_shared = 0;
-
- verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) +
- ELEM(tri_a_co[2], UNPACK3(tri_b_co)));
-
- /* if 2 points are shared, bail out */
- if (verts_shared >= 2) {
- return false;
- }
-
- return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) &&
- /* if we share a vertex, check the intersection isn't a 'point' */
- ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon)));
-}
-
-static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
-{
- BMEditMesh *em = mr->edit_bmesh;
-
- for (int l_index = 0; l_index < mr->loop_len; l_index++) {
- r_intersect[l_index] = -1.0f;
- }
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- uint overlap_len;
- BMesh *bm = em->bm;
-
- BM_mesh_elem_index_ensure(bm, BM_FACE);
-
- struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
- BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len);
-
- if (overlap) {
- for (int i = 0; i < overlap_len; i++) {
- BMFace *f_hit_pair[2] = {
- em->looptris[overlap[i].indexA][0]->f,
- em->looptris[overlap[i].indexB][0]->f,
- };
- for (int j = 0; j < 2; j++) {
- BMFace *f_hit = f_hit_pair[j];
- BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit);
- int l_index = BM_elem_index_get(l_first);
- for (int k = 0; k < f_hit->len; k++, l_index++) {
- r_intersect[l_index] = 1.0f;
- }
- }
- }
- MEM_freeN(overlap);
- }
-
- BKE_bmbvh_free(bmtree);
- }
- else {
- uint overlap_len;
- BVHTreeFromMesh treeData = {NULL};
-
- BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
-
- struct BVHTree_OverlapData data = {
- .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)};
-
- BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data);
- if (overlap) {
- for (int i = 0; i < overlap_len; i++) {
- const MPoly *f_hit_pair[2] = {
- &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly],
- &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly],
- };
- for (int j = 0; j < 2; j++) {
- const MPoly *f_hit = f_hit_pair[j];
- int l_index = f_hit->loopstart;
- for (int k = 0; k < f_hit->totloop; k++, l_index++) {
- r_intersect[l_index] = 1.0f;
- }
- }
- }
- MEM_freeN(overlap);
- }
- }
-}
-
-BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange)
-{
- if (fac >= min) {
- fac = (fac - min) * minmax_irange;
- CLAMP(fac, 0.0f, 1.0f);
- }
- else {
- /* fallback */
- fac = -1.0f;
- }
- return fac;
-}
-
-static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
-{
- BMEditMesh *em = mr->edit_bmesh;
- const MeshStatVis *statvis = &mr->toolsettings->statvis;
- const float min = statvis->distort_min;
- const float max = statvis->distort_max;
- const float minmax_irange = 1.0f / (max - min);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMesh *bm = em->bm;
- BMFace *f;
-
- if (mr->bm_vert_coords != NULL) {
- BKE_editmesh_cache_ensure_poly_normals(em, mr->edit_data);
-
- /* Most likely this is already valid, ensure just in case.
- * Needed for #BM_loop_calc_face_normal_safe_vcos. */
- BM_mesh_elem_index_ensure(em->bm, BM_VERT);
- }
-
- int l_index = 0;
- int f_index = 0;
- BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) {
- float fac = -1.0f;
-
- if (f->len > 3) {
- BMLoop *l_iter, *l_first;
-
- fac = 0.0f;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- const float *no_face;
- float no_corner[3];
- if (mr->bm_vert_coords != NULL) {
- no_face = mr->bm_poly_normals[f_index];
- BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner);
- }
- else {
- no_face = f->no;
- BM_loop_calc_face_normal_safe(l_iter, no_corner);
- }
-
- /* simple way to detect (what is most likely) concave */
- if (dot_v3v3(no_face, no_corner) < 0.0f) {
- negate_v3(no_corner);
- }
- fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner));
-
- } while ((l_iter = l_iter->next) != l_first);
- fac *= 2.0f;
- }
-
- fac = distort_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l_index++) {
- r_distort[l_index] = fac;
- }
- }
- }
- else {
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- float fac = -1.0f;
-
- if (mp->totloop > 3) {
- float *f_no = mr->poly_normals[mp_index];
- fac = 0.0f;
-
- for (int i = 1; i <= mp->totloop; i++) {
- const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop];
- const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
- const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
- float no_corner[3];
- normal_tri_v3(no_corner,
- mr->mvert[l_prev->v].co,
- mr->mvert[l_curr->v].co,
- mr->mvert[l_next->v].co);
- /* simple way to detect (what is most likely) concave */
- if (dot_v3v3(f_no, no_corner) < 0.0f) {
- negate_v3(no_corner);
- }
- fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner));
- }
- fac *= 2.0f;
- }
-
- fac = distort_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mp->totloop; i++, l_index++) {
- r_distort[l_index] = fac;
- }
- }
- }
-}
-
-BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange)
-{
- /* important not '>=' */
- if (fac > min) {
- fac = (fac - min) * minmax_irange;
- CLAMP(fac, 0.0f, 1.0f);
- }
- else {
- /* fallback */
- fac = -1.0f;
- }
- return fac;
-}
-
-static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
-{
- BMEditMesh *em = mr->edit_bmesh;
- const MeshStatVis *statvis = &mr->toolsettings->statvis;
- const float min = statvis->sharp_min;
- const float max = statvis->sharp_max;
- const float minmax_irange = 1.0f / (max - min);
-
- /* Can we avoid this extra allocation? */
- float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__);
- copy_vn_fl(vert_angles, mr->vert_len, -M_PI);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMesh *bm = em->bm;
- BMFace *efa;
- BMEdge *e;
- /* first assign float values to verts */
- BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
- float angle = BM_edge_calc_face_angle_signed(e);
- float *col1 = &vert_angles[BM_elem_index_get(e->v1)];
- float *col2 = &vert_angles[BM_elem_index_get(e->v2)];
- *col1 = max_ff(*col1, angle);
- *col2 = max_ff(*col2, angle);
- }
- /* Copy vert value to loops. */
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- int l_index = BM_elem_index_get(l_iter);
- int v_index = BM_elem_index_get(l_iter->v);
- r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange);
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- /* first assign float values to verts */
- const MPoly *mp = mr->mpoly;
-
- EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len);
-
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- for (int i = 0; i < mp->totloop; i++) {
- const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
- const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
- const MVert *v_curr = &mr->mvert[l_curr->v];
- const MVert *v_next = &mr->mvert[l_next->v];
- float angle;
- void **pval;
- bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval);
- if (!value_is_init) {
- *pval = mr->poly_normals[mp_index];
- /* non-manifold edge, yet... */
- continue;
- }
- if (*pval != NULL) {
- const float *f1_no = mr->poly_normals[mp_index];
- const float *f2_no = *pval;
- angle = angle_normalized_v3v3(f1_no, f2_no);
- angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle;
- /* Tag as manifold. */
- *pval = NULL;
- }
- else {
- /* non-manifold edge */
- angle = DEG2RADF(90.0f);
- }
- float *col1 = &vert_angles[l_curr->v];
- float *col2 = &vert_angles[l_next->v];
- *col1 = max_ff(*col1, angle);
- *col2 = max_ff(*col2, angle);
- }
- }
- /* Remaining non manifold edges. */
- EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh);
- for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
- if (BLI_edgehashIterator_getValue(ehi) != NULL) {
- uint v1, v2;
- const float angle = DEG2RADF(90.0f);
- BLI_edgehashIterator_getKey(ehi, &v1, &v2);
- float *col1 = &vert_angles[v1];
- float *col2 = &vert_angles[v2];
- *col1 = max_ff(*col1, angle);
- *col2 = max_ff(*col2, angle);
- }
- }
- BLI_edgehashIterator_free(ehi);
- BLI_edgehash_free(eh, NULL);
-
- const MLoop *ml = mr->mloop;
- for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) {
- r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange);
- }
- }
-
- MEM_freeN(vert_angles);
-}
-
-static void extract_mesh_analysis_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *UNUSED(data))
-{
- BLI_assert(mr->edit_bmesh);
-
- GPUVertBuf *vbo = buf;
- float *l_weight = (float *)GPU_vertbuf_get_data(vbo);
-
- switch (mr->toolsettings->statvis.type) {
- case SCE_STATVIS_OVERHANG:
- statvis_calc_overhang(mr, l_weight);
- break;
- case SCE_STATVIS_THICKNESS:
- statvis_calc_thickness(mr, l_weight);
- break;
- case SCE_STATVIS_INTERSECT:
- statvis_calc_intersect(mr, l_weight);
- break;
- case SCE_STATVIS_DISTORT:
- statvis_calc_distort(mr, l_weight);
- break;
- case SCE_STATVIS_SHARP:
- statvis_calc_sharp(mr, l_weight);
- break;
- }
-}
-
-static const MeshExtract extract_mesh_analysis = {
- .init = extract_mesh_analysis_init,
- .finish = extract_mesh_analysis_finish,
- /* This is not needed for all visualization types.
- * * Maybe split into different extract. */
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots positions
- * \{ */
-
-static void *extract_fdots_pos_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- float(*center)[3] = data;
-
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- float *co = center[f_index];
- zero_v3(co);
-
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- add_v3_v3(co, bm_vert_co_get(mr, l_iter->v));
- } while ((l_iter = l_iter->next) != l_first);
- mul_v3_fl(co, 1.0f / (float)f->len);
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- float(*center)[3] = (float(*)[3])data;
- const MVert *mvert = mr->mvert;
- const MLoop *mloop = mr->mloop;
-
- if (mr->use_subsurf_fdots) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MVert *mv = &mr->mvert[ml->v];
- if (mv->flag & ME_VERT_FACEDOT) {
- copy_v3_v3(center[mp_index], mv->co);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- float *co = center[mp_index];
- zero_v3(co);
-
- const MLoop *ml = &mloop[mp->loopstart];
- for (int i = 0; i < mp->totloop; i++, ml++) {
- const MVert *mv = &mvert[ml->v];
- add_v3_v3(center[mp_index], mv->co);
- }
- mul_v3_fl(co, 1.0f / (float)mp->totloop);
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static const MeshExtract extract_fdots_pos = {
- .init = extract_fdots_pos_init,
- .iter_poly_bm = extract_fdots_pos_iter_poly_bm,
- .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Normal and edit flag
- * \{ */
-#define NOR_AND_FLAG_DEFAULT 0
-#define NOR_AND_FLAG_SELECT 1
-#define NOR_AND_FLAG_ACTIVE -1
-#define NOR_AND_FLAG_HIDDEN -2
-
-static void *extract_fdots_nor_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
-
- return NULL;
-}
-
-static void extract_fdots_nor_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *UNUSED(data))
-{
- static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
- GPUVertBuf *vbo = buf;
- GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
- BMFace *efa;
-
- /* Quicker than doing it for each loop. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- for (int f = 0; f < mr->poly_len; f++) {
- efa = BM_face_at_index(mr->bm, f);
- const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
- if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[f] == ORIGINDEX_NONE)) {
- nor[f] = GPU_normal_convert_i10_v3(invalid_normal);
- nor[f].w = NOR_AND_FLAG_HIDDEN;
- }
- else {
- nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
- /* Select / Active Flag. */
- nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
- ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
- NOR_AND_FLAG_DEFAULT);
- }
- }
- }
- else {
- for (int f = 0; f < mr->poly_len; f++) {
- efa = bm_original_face_get(mr, f);
- const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
- if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[f] == ORIGINDEX_NONE)) {
- nor[f] = GPU_normal_convert_i10_v3(invalid_normal);
- nor[f].w = NOR_AND_FLAG_HIDDEN;
- }
- else {
- nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
- /* Select / Active Flag. */
- nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
- ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
- NOR_AND_FLAG_DEFAULT);
- }
- }
- }
-}
-
-static const MeshExtract extract_fdots_nor = {
- .init = extract_fdots_nor_init,
- .finish = extract_fdots_nor_finish,
- .data_flag = MR_DATA_POLY_NOR,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots High Quality Normal and edit flag
- * \{ */
-static void *extract_fdots_nor_hq_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
-
- return NULL;
-}
-
-static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *UNUSED(data))
-{
- static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
- GPUVertBuf *vbo = buf;
- short *nor = (short *)GPU_vertbuf_get_data(vbo);
- BMFace *efa;
-
- /* Quicker than doing it for each loop. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- for (int f = 0; f < mr->poly_len; f++) {
- efa = BM_face_at_index(mr->bm, f);
- const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
- if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[f] == ORIGINDEX_NONE)) {
- normal_float_to_short_v3(&nor[f * 4], invalid_normal);
- nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
- }
- else {
- normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
- /* Select / Active Flag. */
- nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
- ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
- NOR_AND_FLAG_DEFAULT);
- }
- }
- }
- else {
- for (int f = 0; f < mr->poly_len; f++) {
- efa = bm_original_face_get(mr, f);
- const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
- if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[f] == ORIGINDEX_NONE)) {
- normal_float_to_short_v3(&nor[f * 4], invalid_normal);
- nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
- }
- else {
- normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
- /* Select / Active Flag. */
- nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
- ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
- NOR_AND_FLAG_DEFAULT);
- }
- }
- }
-}
-
-static const MeshExtract extract_fdots_nor_hq = {
- .init = extract_fdots_nor_hq_init,
- .finish = extract_fdots_nor_hq_finish,
- .data_flag = MR_DATA_POLY_NOR,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots UV
- * \{ */
-
-typedef struct MeshExtract_FdotUV_Data {
- float (*vbo_data)[2];
- MLoopUV *uv_data;
- int cd_ofs;
-} MeshExtract_FdotUV_Data;
-
-static void *extract_fdots_uv_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- GPU_vertformat_alias_add(&format, "au");
- GPU_vertformat_alias_add(&format, "pos");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
-
- if (!mr->use_subsurf_fdots) {
- /* Clear so we can accumulate on it. */
- memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride);
- }
-
- MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
- }
- else {
- data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
- }
- return data;
-}
-
-static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_FdotUV_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- float w = 1.0f / (float)l->f->len;
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs);
- madd_v2_v2fl(data->vbo_data[BM_elem_index_get(l->f)], luv->uv, w);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_FdotUV_Data *data = _data;
- if (mr->use_subsurf_fdots) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MVert *mv = &mr->mvert[ml->v];
- if (mv->flag & ME_VERT_FACEDOT) {
- copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- float w = 1.0f / (float)mp->totloop;
- madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
-}
-
-static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_fdots_uv = {
- .init = extract_fdots_uv_init,
- .iter_poly_bm = extract_fdots_uv_iter_poly_bm,
- .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh,
- .finish = extract_fdots_uv_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Edit UV flag
- * \{ */
-
-typedef struct MeshExtract_EditUVFdotData_Data {
- EditLoopData *vbo_data;
- int cd_ofs;
-} MeshExtract_EditUVFdotData_Data;
-
-static void *extract_fdots_edituv_data_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
-
- MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
- data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
- return data;
-}
-
-static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_EditUVFdotData_Data *data = _data;
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)];
- memset(eldata, 0x0, sizeof(*eldata));
- mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata);
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_EditUVFdotData_Data *data = _data;
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- EditLoopData *eldata = &data->vbo_data[mp_index];
- memset(eldata, 0x0, sizeof(*eldata));
- BMFace *efa = bm_original_face_get(mr, mp_index);
- if (efa) {
- mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata);
- }
- }
- EXTRACT_POLY_FOREACH_MESH_END;
-}
-
-static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_fdots_edituv_data = {
- .init = extract_fdots_edituv_data_init,
- .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm,
- .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh,
- .finish = extract_fdots_edituv_data_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Skin Modifier Roots
- * \{ */
-
-typedef struct SkinRootData {
- float size;
- float local_pos[3];
-} SkinRootData;
-
-static void *extract_skin_roots_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- /* Exclusively for edit mode. */
- BLI_assert(mr->bm);
-
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->bm->totvert);
-
- SkinRootData *vbo_data = (SkinRootData *)GPU_vertbuf_get_data(vbo);
-
- int root_len = 0;
- int cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MVERT_SKIN);
-
- BMIter iter;
- BMVert *eve;
- BM_ITER_MESH (eve, &iter, mr->bm, BM_VERTS_OF_MESH) {
- const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_ofs);
- if (vs->flag & MVERT_SKIN_ROOT) {
- vbo_data->size = (vs->radius[0] + vs->radius[1]) * 0.5f;
- copy_v3_v3(vbo_data->local_pos, bm_vert_co_get(mr, eve));
- vbo_data++;
- root_len++;
- }
- }
-
- /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */
- GPU_vertbuf_data_len_set(vbo, root_len);
-
- return NULL;
-}
-
-static const MeshExtract extract_skin_roots = {
- .init = extract_skin_roots_init,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Selection Index
- * \{ */
-
-static void *extract_select_idx_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* TODO rename "color" to something more descriptive. */
- GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
- return GPU_vertbuf_get_data(vbo);
-}
-
-/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the
- * select element associated with this loop ID. This would remove the need for this separate
- * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the
- * shader to output original index. */
-
-static void extract_poly_idx_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- ((uint32_t *)data)[l_index] = BM_elem_index_get(l->f);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edge_idx_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- ((uint32_t *)data)[l_index] = BM_elem_index_get(l->e);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_vert_idx_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- ((uint32_t *)data)[l_index] = BM_elem_index_get(l->v);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *data)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed);
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed);
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *data)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1);
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2);
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *data)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve);
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *data)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- const int e_index = mr->ledges[ledge_index];
- const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index;
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig;
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *data)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1;
- int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2;
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig;
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *data)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(med, lvert_index, params, mr)
- {
- const int v_index = mr->lverts[lvert_index];
- const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index;
- ((uint32_t *)data)[offset + lvert_index] = v_orig;
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static const MeshExtract extract_poly_idx = {
- .init = extract_select_idx_init,
- .iter_poly_bm = extract_poly_idx_iter_poly_bm,
- .iter_poly_mesh = extract_poly_idx_iter_poly_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-static const MeshExtract extract_edge_idx = {
- .init = extract_select_idx_init,
- .iter_poly_bm = extract_edge_idx_iter_poly_bm,
- .iter_poly_mesh = extract_edge_idx_iter_poly_mesh,
- .iter_ledge_bm = extract_edge_idx_iter_ledge_bm,
- .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-static const MeshExtract extract_vert_idx = {
- .init = extract_select_idx_init,
- .iter_poly_bm = extract_vert_idx_iter_poly_bm,
- .iter_poly_mesh = extract_vert_idx_iter_poly_mesh,
- .iter_ledge_bm = extract_vert_idx_iter_ledge_bm,
- .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh,
- .iter_lvert_bm = extract_vert_idx_iter_lvert_bm,
- .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-static void *extract_select_fdot_idx_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* TODO rename "color" to something more descriptive. */
- GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- ((uint32_t *)data)[f_index] = f_index;
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- if (mr->p_origindex != NULL) {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index];
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- ((uint32_t *)data)[mp_index] = mp_index;
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static const MeshExtract extract_fdot_idx = {
- .init = extract_select_fdot_idx_init,
- .iter_poly_bm = extract_fdot_idx_iter_poly_bm,
- .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name ExtractTaskData
- * \{ */
-typedef struct ExtractUserData {
- void *user_data;
-} ExtractUserData;
-
-typedef enum ExtractTaskDataType {
- EXTRACT_MESH_EXTRACT,
- EXTRACT_LINES_LOOSE,
-} ExtractTaskDataType;
-
-typedef struct ExtractTaskData {
- void *next, *prev;
- const MeshRenderData *mr;
- struct MeshBatchCache *cache;
- const MeshExtract *extract;
- ExtractTaskDataType tasktype;
- eMRIterType iter_type;
- int start, end;
- /** Decremented each time a task is finished. */
- int32_t *task_counter;
- void *buf;
- ExtractUserData *user_data;
-} ExtractTaskData;
-
-static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- const MeshExtract *extract,
- void *buf,
- int32_t *task_counter)
-{
- ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__);
- taskdata->next = NULL;
- taskdata->prev = NULL;
- taskdata->tasktype = EXTRACT_MESH_EXTRACT;
- taskdata->mr = mr;
- taskdata->cache = cache;
- taskdata->extract = extract;
- taskdata->buf = buf;
-
- /* #ExtractUserData is shared between the iterations as it holds counters to detect if the
- * extraction is finished. To make sure the duplication of the user_data does not create a new
- * instance of the counters we allocate the user_data in its own container.
- *
- * This structure makes sure that when extract_init is called, that the user data of all
- * iterations are updated. */
- taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__);
- taskdata->iter_type = mesh_extract_iter_type(extract);
- taskdata->task_counter = task_counter;
- taskdata->start = 0;
- taskdata->end = INT_MAX;
- return taskdata;
-}
-
-static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr,
- struct MeshBatchCache *cache)
-{
- ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__);
- taskdata->tasktype = EXTRACT_LINES_LOOSE;
- taskdata->mr = mr;
- taskdata->cache = cache;
- return taskdata;
-}
-
-static void extract_task_data_free(void *data)
-{
- ExtractTaskData *task_data = data;
- MEM_SAFE_FREE(task_data->user_data);
- MEM_freeN(task_data);
-}
-
-BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
- const eMRIterType iter_type,
- int start,
- int end,
- const MeshExtract *extract,
- void *user_data)
-{
- switch (mr->extract_type) {
- case MR_EXTRACT_BMESH:
- if (iter_type & MR_ITER_LOOPTRI) {
- extract->iter_looptri_bm(mr,
- &(const ExtractTriBMesh_Params){
- .looptris = mr->edit_bmesh->looptris,
- .tri_range = {start, min_ii(mr->tri_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_POLY) {
- extract->iter_poly_bm(mr,
- &(const ExtractPolyBMesh_Params){
- .poly_range = {start, min_ii(mr->poly_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_LEDGE) {
- extract->iter_ledge_bm(mr,
- &(const ExtractLEdgeBMesh_Params){
- .ledge = mr->ledges,
- .ledge_range = {start, min_ii(mr->edge_loose_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_LVERT) {
- extract->iter_lvert_bm(mr,
- &(const ExtractLVertBMesh_Params){
- .lvert = mr->lverts,
- .lvert_range = {start, min_ii(mr->vert_loose_len, end)},
- },
- user_data);
- }
- break;
- case MR_EXTRACT_MAPPED:
- case MR_EXTRACT_MESH:
- if (iter_type & MR_ITER_LOOPTRI) {
- extract->iter_looptri_mesh(mr,
- &(const ExtractTriMesh_Params){
- .mlooptri = mr->mlooptri,
- .tri_range = {start, min_ii(mr->tri_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_POLY) {
- extract->iter_poly_mesh(mr,
- &(const ExtractPolyMesh_Params){
- .poly_range = {start, min_ii(mr->poly_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_LEDGE) {
- extract->iter_ledge_mesh(mr,
- &(const ExtractLEdgeMesh_Params){
- .ledge = mr->ledges,
- .ledge_range = {start, min_ii(mr->edge_loose_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_LVERT) {
- extract->iter_lvert_mesh(mr,
- &(const ExtractLVertMesh_Params){
- .lvert = mr->lverts,
- .lvert_range = {start, min_ii(mr->vert_loose_len, end)},
- },
- user_data);
- }
- break;
- }
-}
-
-static void extract_init(ExtractTaskData *data)
-{
- if (data->tasktype == EXTRACT_MESH_EXTRACT) {
- data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf);
- }
-}
-
-static void extract_run(void *__restrict taskdata)
-{
- ExtractTaskData *data = (ExtractTaskData *)taskdata;
- if (data->tasktype == EXTRACT_MESH_EXTRACT) {
- mesh_extract_iter(data->mr,
- data->iter_type,
- data->start,
- data->end,
- data->extract,
- data->user_data->user_data);
-
- /* If this is the last task, we do the finish function. */
- int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1);
- if (remainin_tasks == 0 && data->extract->finish != NULL) {
- data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data);
- }
- }
- else if (data->tasktype == EXTRACT_LINES_LOOSE) {
- extract_lines_loose_subbuffer(data->mr, data->cache);
- }
-}
-
-static void extract_init_and_run(void *__restrict taskdata)
-{
- extract_init((ExtractTaskData *)taskdata);
- extract_run(taskdata);
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Task Node - Update Mesh Render Data
- * \{ */
-typedef struct MeshRenderDataUpdateTaskData {
- MeshRenderData *mr;
- eMRIterType iter_type;
- eMRDataType data_flag;
-} MeshRenderDataUpdateTaskData;
-
-static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata)
-{
- BLI_assert(taskdata);
- MeshRenderData *mr = taskdata->mr;
- mesh_render_data_free(mr);
- MEM_freeN(taskdata);
-}
-
-static void mesh_extract_render_data_node_exec(void *__restrict task_data)
-{
- MeshRenderDataUpdateTaskData *update_task_data = task_data;
- MeshRenderData *mr = update_task_data->mr;
- const eMRIterType iter_type = update_task_data->iter_type;
- const eMRDataType data_flag = update_task_data->data_flag;
-
- mesh_render_data_update_normals(mr, iter_type, data_flag);
- mesh_render_data_update_looptris(mr, iter_type, data_flag);
-}
-
-static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
- MeshRenderData *mr,
- const eMRIterType iter_type,
- const eMRDataType data_flag)
-{
- MeshRenderDataUpdateTaskData *task_data = MEM_mallocN(sizeof(MeshRenderDataUpdateTaskData),
- __func__);
- task_data->mr = mr;
- task_data->iter_type = iter_type;
- task_data->data_flag = data_flag;
-
- struct TaskNode *task_node = BLI_task_graph_node_create(
- task_graph,
- mesh_extract_render_data_node_exec,
- task_data,
- (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free);
- return task_node;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Task Node - Extract Single Threaded
- * \{ */
-typedef struct ExtractSingleThreadedTaskData {
- ListBase task_datas;
-} ExtractSingleThreadedTaskData;
-
-static void extract_single_threaded_task_data_free(ExtractSingleThreadedTaskData *taskdata)
-{
- BLI_assert(taskdata);
- LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) {
- extract_task_data_free(td);
- }
- BLI_listbase_clear(&taskdata->task_datas);
- MEM_freeN(taskdata);
-}
-
-static void extract_single_threaded_task_node_exec(void *__restrict task_data)
-{
- ExtractSingleThreadedTaskData *extract_task_data = task_data;
- LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) {
- extract_init_and_run(td);
- }
-}
-
-static struct TaskNode *extract_single_threaded_task_node_create(
- struct TaskGraph *task_graph, ExtractSingleThreadedTaskData *task_data)
-{
- struct TaskNode *task_node = BLI_task_graph_node_create(
- task_graph,
- extract_single_threaded_task_node_exec,
- task_data,
- (TaskGraphNodeFreeFunction)extract_single_threaded_task_data_free);
- return task_node;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Task Node - UserData Initializer
- * \{ */
-typedef struct UserDataInitTaskData {
- ListBase task_datas;
- int32_t *task_counters;
-
-} UserDataInitTaskData;
-
-static void user_data_init_task_data_free(UserDataInitTaskData *taskdata)
-{
- BLI_assert(taskdata);
- LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) {
- extract_task_data_free(td);
- }
- BLI_listbase_clear(&taskdata->task_datas);
- MEM_SAFE_FREE(taskdata->task_counters);
- MEM_freeN(taskdata);
-}
-
-static void user_data_init_task_data_exec(void *__restrict task_data)
-{
- UserDataInitTaskData *extract_task_data = task_data;
- LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) {
- extract_init(td);
- }
-}
-
-static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph,
- UserDataInitTaskData *task_data)
-{
- struct TaskNode *task_node = BLI_task_graph_node_create(
- task_graph,
- user_data_init_task_data_exec,
- task_data,
- (TaskGraphNodeFreeFunction)user_data_init_task_data_free);
- return task_node;
-}
-
-/** \} */
-/* ---------------------------------------------------------------------- */
-/** \name Extract Loop
- * \{ */
-
-static void extract_range_task_create(struct TaskGraph *task_graph,
- struct TaskNode *task_node_user_data_init,
- ExtractTaskData *taskdata,
- const eMRIterType type,
- int start,
- int length)
-{
- taskdata = MEM_dupallocN(taskdata);
- atomic_add_and_fetch_int32(taskdata->task_counter, 1);
- taskdata->iter_type = type;
- taskdata->start = start;
- taskdata->end = start + length;
- struct TaskNode *task_node = BLI_task_graph_node_create(
- task_graph, extract_run, taskdata, MEM_freeN);
- BLI_task_graph_edge_create(task_node_user_data_init, task_node);
-}
-
-static void extract_task_create(struct TaskGraph *task_graph,
- struct TaskNode *task_node_mesh_render_data,
- struct TaskNode *task_node_user_data_init,
- ListBase *single_threaded_task_datas,
- ListBase *user_data_init_task_datas,
- const Scene *scene,
- const MeshRenderData *mr,
- MeshBatchCache *cache,
- const MeshExtract *extract,
- void *buf,
- int32_t *task_counter)
-{
- BLI_assert(scene != NULL);
- const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
- GPU_use_hq_normals_workaround();
- if (do_hq_normals) {
- if (extract == &extract_lnor) {
- extract = &extract_lnor_hq;
- }
- else if (extract == &extract_pos_nor) {
- extract = &extract_pos_nor_hq;
- }
- else if (extract == &extract_tan) {
- extract = &extract_tan_hq;
- }
- else if (extract == &extract_fdots_nor) {
- extract = &extract_fdots_nor_hq;
- }
- }
-
- /* Divide extraction of the VBO/IBO into sensible chunks of works. */
- ExtractTaskData *taskdata = extract_task_data_create_mesh_extract(
- mr, cache, extract, buf, task_counter);
-
- /* Simple heuristic. */
- const int chunk_size = 8192;
- const bool use_thread = (mr->loop_len + mr->loop_loose_len) > chunk_size;
- if (use_thread && extract->use_threading) {
-
- /* Divide task into sensible chunks. */
- if (taskdata->iter_type & MR_ITER_LOOPTRI) {
- for (int i = 0; i < mr->tri_len; i += chunk_size) {
- extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOPTRI, i, chunk_size);
- }
- }
- if (taskdata->iter_type & MR_ITER_POLY) {
- for (int i = 0; i < mr->poly_len; i += chunk_size) {
- extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_POLY, i, chunk_size);
- }
- }
- if (taskdata->iter_type & MR_ITER_LEDGE) {
- for (int i = 0; i < mr->edge_loose_len; i += chunk_size) {
- extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_LEDGE, i, chunk_size);
- }
- }
- if (taskdata->iter_type & MR_ITER_LVERT) {
- for (int i = 0; i < mr->vert_loose_len; i += chunk_size) {
- extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_LVERT, i, chunk_size);
- }
- }
- BLI_addtail(user_data_init_task_datas, taskdata);
- }
- else if (use_thread) {
- /* One task for the whole VBO. */
- (*task_counter)++;
- struct TaskNode *one_task = BLI_task_graph_node_create(
- task_graph, extract_init_and_run, taskdata, extract_task_data_free);
- BLI_task_graph_edge_create(task_node_mesh_render_data, one_task);
- }
- else {
- /* Single threaded extraction. */
- (*task_counter)++;
- BLI_addtail(single_threaded_task_datas, taskdata);
- }
-}
-
-void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
- MeshBatchCache *cache,
- MeshBufferCache mbc,
- Mesh *me,
-
- const bool is_editmode,
- const bool is_paint_mode,
- const bool is_mode_active,
- const float obmat[4][4],
- const bool do_final,
- const bool do_uvedit,
- const bool use_subsurf_fdots,
- const DRW_MeshCDMask *cd_layer_used,
- const Scene *scene,
- const ToolSettings *ts,
- const bool use_hide)
-{
- /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph.
- * This sub-graph starts with an extract_render_data_node. This fills/converts the required data
- * from Mesh.
- *
- * Small extractions and extractions that can't be multi-threaded are grouped in a single
- * `extract_single_threaded_task_node`.
- *
- * Other extractions will create a node for each loop exceeding 8192 items. these nodes are
- * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the
- * user_data needed for the extraction based on the data extracted from the mesh.
- * counters are used to check if the finalize of a task has to be called.
- *
- * Mesh extraction sub graph
- *
- * +----------------------+
- * +-----> | extract_task1_loop_1 |
- * | +----------------------+
- * +------------------+ +----------------------+ +----------------------+
- * | mesh_render_data | --> | | --> | extract_task1_loop_2 |
- * +------------------+ | | +----------------------+
- * | | | +----------------------+
- * | | user_data_init | --> | extract_task2_loop_1 |
- * v | | +----------------------+
- * +------------------+ | | +----------------------+
- * | single_threaded | | | --> | extract_task2_loop_2 |
- * +------------------+ +----------------------+ +----------------------+
- * | +----------------------+
- * +-----> | extract_task2_loop_3 |
- * +----------------------+
- */
- eMRIterType iter_flag = 0;
- eMRDataType data_flag = 0;
-
- const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL;
-
-#define TEST_ASSIGN(type, type_lowercase, name) \
- do { \
- if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \
- iter_flag |= mesh_extract_iter_type(&extract_##name); \
- data_flag |= extract_##name.data_flag; \
- } \
- } while (0)
-
- TEST_ASSIGN(VBO, vbo, pos_nor);
- TEST_ASSIGN(VBO, vbo, lnor);
- TEST_ASSIGN(VBO, vbo, uv);
- TEST_ASSIGN(VBO, vbo, tan);
- TEST_ASSIGN(VBO, vbo, vcol);
- TEST_ASSIGN(VBO, vbo, sculpt_data);
- TEST_ASSIGN(VBO, vbo, orco);
- TEST_ASSIGN(VBO, vbo, edge_fac);
- TEST_ASSIGN(VBO, vbo, weights);
- TEST_ASSIGN(VBO, vbo, edit_data);
- TEST_ASSIGN(VBO, vbo, edituv_data);
- TEST_ASSIGN(VBO, vbo, edituv_stretch_area);
- TEST_ASSIGN(VBO, vbo, edituv_stretch_angle);
- TEST_ASSIGN(VBO, vbo, mesh_analysis);
- TEST_ASSIGN(VBO, vbo, fdots_pos);
- TEST_ASSIGN(VBO, vbo, fdots_nor);
- TEST_ASSIGN(VBO, vbo, fdots_uv);
- TEST_ASSIGN(VBO, vbo, fdots_edituv_data);
- TEST_ASSIGN(VBO, vbo, poly_idx);
- TEST_ASSIGN(VBO, vbo, edge_idx);
- TEST_ASSIGN(VBO, vbo, vert_idx);
- TEST_ASSIGN(VBO, vbo, fdot_idx);
- TEST_ASSIGN(VBO, vbo, skin_roots);
-
- TEST_ASSIGN(IBO, ibo, tris);
- TEST_ASSIGN(IBO, ibo, lines);
- TEST_ASSIGN(IBO, ibo, points);
- TEST_ASSIGN(IBO, ibo, fdots);
- TEST_ASSIGN(IBO, ibo, lines_paint_mask);
- TEST_ASSIGN(IBO, ibo, lines_adjacency);
- TEST_ASSIGN(IBO, ibo, edituv_tris);
- TEST_ASSIGN(IBO, ibo, edituv_lines);
- TEST_ASSIGN(IBO, ibo, edituv_points);
- TEST_ASSIGN(IBO, ibo, edituv_fdots);
-
- if (do_lines_loose_subbuffer) {
- iter_flag |= MR_ITER_LEDGE;
- }
-
-#undef TEST_ASSIGN
-
-#ifdef DEBUG_TIME
- double rdata_start = PIL_check_seconds_timer();
-#endif
-
- MeshRenderData *mr = mesh_render_data_create(me,
- is_editmode,
- is_paint_mode,
- is_mode_active,
- obmat,
- do_final,
- do_uvedit,
- cd_layer_used,
- ts,
- iter_flag,
- data_flag);
- mr->use_hide = use_hide;
- mr->use_subsurf_fdots = use_subsurf_fdots;
- mr->use_final_mesh = do_final;
-
-#ifdef DEBUG_TIME
- double rdata_end = PIL_check_seconds_timer();
-#endif
-
- size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t);
- int32_t *task_counters = MEM_callocN(counters_size, __func__);
- int counter_used = 0;
-
- struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
- task_graph, mr, iter_flag, data_flag);
- ExtractSingleThreadedTaskData *single_threaded_task_data = MEM_callocN(
- sizeof(ExtractSingleThreadedTaskData), __func__);
- UserDataInitTaskData *user_data_init_task_data = MEM_callocN(sizeof(UserDataInitTaskData),
- __func__);
- user_data_init_task_data->task_counters = task_counters;
- struct TaskNode *task_node_user_data_init = user_data_init_task_node_create(
- task_graph, user_data_init_task_data);
-
-#define EXTRACT(buf, name) \
- if (mbc.buf.name) { \
- extract_task_create(task_graph, \
- task_node_mesh_render_data, \
- task_node_user_data_init, \
- &single_threaded_task_data->task_datas, \
- &user_data_init_task_data->task_datas, \
- scene, \
- mr, \
- cache, \
- &extract_##name, \
- mbc.buf.name, \
- &task_counters[counter_used++]); \
- } \
- ((void)0)
-
- EXTRACT(vbo, pos_nor);
- EXTRACT(vbo, lnor);
- EXTRACT(vbo, uv);
- EXTRACT(vbo, tan);
- EXTRACT(vbo, vcol);
- EXTRACT(vbo, sculpt_data);
- EXTRACT(vbo, orco);
- EXTRACT(vbo, edge_fac);
- EXTRACT(vbo, weights);
- EXTRACT(vbo, edit_data);
- EXTRACT(vbo, edituv_data);
- EXTRACT(vbo, edituv_stretch_area);
- EXTRACT(vbo, edituv_stretch_angle);
- EXTRACT(vbo, mesh_analysis);
- EXTRACT(vbo, fdots_pos);
- EXTRACT(vbo, fdots_nor);
- EXTRACT(vbo, fdots_uv);
- EXTRACT(vbo, fdots_edituv_data);
- EXTRACT(vbo, poly_idx);
- EXTRACT(vbo, edge_idx);
- EXTRACT(vbo, vert_idx);
- EXTRACT(vbo, fdot_idx);
- EXTRACT(vbo, skin_roots);
-
- EXTRACT(ibo, tris);
- if (mbc.ibo.lines) {
- /* When `lines` and `lines_loose` are requested, schedule lines extraction that also creates
- * the `lines_loose` sub-buffer. */
- const MeshExtract *lines_extractor = do_lines_loose_subbuffer ?
- &extract_lines_with_lines_loose :
- &extract_lines;
- extract_task_create(task_graph,
- task_node_mesh_render_data,
- task_node_user_data_init,
- &single_threaded_task_data->task_datas,
- &user_data_init_task_data->task_datas,
- scene,
- mr,
- cache,
- lines_extractor,
- mbc.ibo.lines,
- &task_counters[counter_used++]);
- }
- else {
- if (do_lines_loose_subbuffer) {
- ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache);
- BLI_addtail(&single_threaded_task_data->task_datas, taskdata);
- }
- }
- EXTRACT(ibo, points);
- EXTRACT(ibo, fdots);
- EXTRACT(ibo, lines_paint_mask);
- EXTRACT(ibo, lines_adjacency);
- EXTRACT(ibo, edituv_tris);
- EXTRACT(ibo, edituv_lines);
- EXTRACT(ibo, edituv_points);
- EXTRACT(ibo, edituv_fdots);
-
- /* Only create the edge when there is user data that needs to be initialized.
- * The task is still part of the graph so the task_data will be freed when the graph is freed.
- */
- if (!BLI_listbase_is_empty(&user_data_init_task_data->task_datas)) {
- BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init);
- }
-
- if (!BLI_listbase_is_empty(&single_threaded_task_data->task_datas)) {
- struct TaskNode *task_node = extract_single_threaded_task_node_create(
- task_graph, single_threaded_task_data);
- BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
- }
- else {
- extract_single_threaded_task_data_free(single_threaded_task_data);
- }
-
- /* Trigger the sub-graph for this mesh. */
- BLI_task_graph_node_push_work(task_node_mesh_render_data);
-
-#undef EXTRACT
-
-#ifdef DEBUG_TIME
- BLI_task_graph_work_and_wait(task_graph);
- double end = PIL_check_seconds_timer();
-
- static double avg = 0;
- static double avg_fps = 0;
- static double avg_rdata = 0;
- static double end_prev = 0;
-
- if (end_prev == 0) {
- end_prev = end;
- }
-
- avg = avg * 0.95 + (end - rdata_end) * 0.05;
- avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05;
- avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05;
-
- printf(
- "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000);
-
- end_prev = end;
-#endif
-}
-
-/** \} */
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
new file mode 100644
index 00000000000..c6b749fe11a
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -0,0 +1,813 @@
+/*
+ * 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 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+#include "MEM_guardedalloc.h"
+
+#include <optional>
+
+#include "atomic_ops.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_array.hh"
+#include "BLI_math_bits.h"
+#include "BLI_task.h"
+#include "BLI_vector.hh"
+
+#include "BKE_editmesh.h"
+
+#include "GPU_capabilities.h"
+
+#include "draw_cache_extract.h"
+#include "draw_cache_extract_mesh_private.h"
+#include "draw_cache_inline.h"
+
+// #define DEBUG_TIME
+
+#ifdef DEBUG_TIME
+# include "PIL_time_utildefines.h"
+#endif
+
+#define MIM_RANGE_LEN 1024
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract Struct
+ * \{ */
+using TaskId = int;
+using TaskLen = int;
+
+struct ExtractorRunData {
+ /* Extractor where this run data belongs to. */
+ const MeshExtract *extractor;
+ /* During iteration the VBO/IBO that is being build. */
+ void *buffer = nullptr;
+ uint32_t data_offset = 0;
+
+ ExtractorRunData(const MeshExtract *extractor) : extractor(extractor)
+ {
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunData")
+#endif
+};
+
+class ExtractorRunDatas : public Vector<ExtractorRunData> {
+ public:
+ void filter_into(ExtractorRunDatas &result, eMRIterType iter_type) const
+ {
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) {
+ BLI_assert(extractor->iter_looptri_mesh);
+ result.append(data);
+ continue;
+ }
+ if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) {
+ BLI_assert(extractor->iter_poly_mesh);
+ result.append(data);
+ continue;
+ }
+ if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) {
+ BLI_assert(extractor->iter_ledge_mesh);
+ result.append(data);
+ continue;
+ }
+ if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) {
+ BLI_assert(extractor->iter_lvert_mesh);
+ result.append(data);
+ continue;
+ }
+ }
+ }
+
+ void filter_threaded_extractors_into(ExtractorRunDatas &result)
+ {
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ if (extractor->use_threading) {
+ result.append(extractor);
+ }
+ }
+ }
+
+ eMRIterType iter_types() const
+ {
+ eMRIterType iter_type = static_cast<eMRIterType>(0);
+
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ iter_type |= mesh_extract_iter_type(extractor);
+ }
+ return iter_type;
+ }
+
+ const uint iter_types_len() const
+ {
+ const eMRIterType iter_type = iter_types();
+ uint bits = static_cast<uint>(iter_type);
+ return count_bits_i(bits);
+ }
+
+ eMRDataType data_types()
+ {
+ eMRDataType data_type = static_cast<eMRDataType>(0);
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ data_type |= extractor->data_type;
+ }
+ return data_type;
+ }
+
+ size_t data_size_total()
+ {
+ size_t data_size = 0;
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ data_size += extractor->data_size;
+ }
+ return data_size;
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunDatas")
+#endif
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name ExtractTaskData
+ * \{ */
+struct ExtractTaskData {
+ const MeshRenderData *mr = nullptr;
+ MeshBatchCache *cache = nullptr;
+ ExtractorRunDatas *extractors = nullptr;
+ MeshBufferCache *mbc = nullptr;
+
+ eMRIterType iter_type;
+ bool use_threading = false;
+
+ ExtractTaskData(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ ExtractorRunDatas *extractors,
+ MeshBufferCache *mbc,
+ const bool use_threading)
+ : mr(mr), cache(cache), extractors(extractors), mbc(mbc), use_threading(use_threading)
+ {
+ iter_type = extractors->iter_types();
+ };
+
+ ExtractTaskData(const ExtractTaskData &src) = default;
+
+ ~ExtractTaskData()
+ {
+ delete extractors;
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData")
+#endif
+};
+
+static void extract_task_data_free(void *data)
+{
+ ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data);
+ delete task_data;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Init and Finish
+ * \{ */
+
+BLI_INLINE void extract_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ ExtractorRunDatas &extractors,
+ MeshBufferCache *mbc,
+ void *data_stack)
+{
+ uint32_t data_offset = 0;
+ for (ExtractorRunData &run_data : extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ run_data.buffer = mesh_extract_buffer_get(extractor, mbc);
+ run_data.data_offset = data_offset;
+ extractor->init(mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, data_offset));
+ data_offset += (uint32_t)extractor->data_size;
+ }
+}
+
+BLI_INLINE void extract_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ const ExtractorRunDatas &extractors,
+ void *data_stack)
+{
+ for (const ExtractorRunData &run_data : extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ if (extractor->finish) {
+ extractor->finish(
+ mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, run_data.data_offset));
+ }
+ }
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract In Parallel Ranges
+ * \{ */
+
+struct ExtractorIterData {
+ ExtractorRunDatas extractors;
+ const MeshRenderData *mr = nullptr;
+ const void *elems = nullptr;
+ const int *loose_elems = nullptr;
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData")
+#endif
+};
+
+static void extract_task_reduce(const void *__restrict userdata,
+ void *__restrict chunk_to,
+ void *__restrict chunk_from)
+{
+ const ExtractorIterData *data = static_cast<const ExtractorIterData *>(userdata);
+ for (const ExtractorRunData &run_data : data->extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ if (extractor->task_reduce) {
+ extractor->task_reduce(POINTER_OFFSET(chunk_to, run_data.data_offset),
+ POINTER_OFFSET(chunk_from, run_data.data_offset));
+ }
+ }
+}
+
+static void extract_range_iter_looptri_bm(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ void *extract_data = tls->userdata_chunk;
+ const MeshRenderData *mr = data->mr;
+ BMLoop **elt = ((BMLoop * (*)[3]) data->elems)[iter];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_looptri_bm(
+ mr, elt, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_looptri_mesh(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const MLoopTri *mlt = &((const MLoopTri *)data->elems)[iter];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_looptri_mesh(
+ mr, mlt, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_poly_bm(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const BMFace *f = ((const BMFace **)data->elems)[iter];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_poly_bm(
+ mr, f, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_poly_mesh(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const MPoly *mp = &((const MPoly *)data->elems)[iter];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_poly_mesh(
+ mr, mp, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_ledge_bm(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const int ledge_index = data->loose_elems[iter];
+ const BMEdge *eed = ((const BMEdge **)data->elems)[ledge_index];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_ledge_bm(
+ mr, eed, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_ledge_mesh(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const int ledge_index = data->loose_elems[iter];
+ const MEdge *med = &((const MEdge *)data->elems)[ledge_index];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_ledge_mesh(
+ mr, med, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_lvert_bm(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const int lvert_index = data->loose_elems[iter];
+ const BMVert *eve = ((const BMVert **)data->elems)[lvert_index];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_lvert_bm(
+ mr, eve, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_lvert_mesh(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const int lvert_index = data->loose_elems[iter];
+ const MVert *mv = &((const MVert *)data->elems)[lvert_index];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_lvert_mesh(
+ mr, mv, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+BLI_INLINE void extract_task_range_run_iter(const MeshRenderData *mr,
+ ExtractorRunDatas *extractors,
+ const eMRIterType iter_type,
+ bool is_mesh,
+ TaskParallelSettings *settings)
+{
+ ExtractorIterData range_data;
+ range_data.mr = mr;
+
+ TaskParallelRangeFunc func;
+ int stop;
+ switch (iter_type) {
+ case MR_ITER_LOOPTRI:
+ range_data.elems = is_mesh ? mr->mlooptri : (void *)mr->edit_bmesh->looptris;
+ func = is_mesh ? extract_range_iter_looptri_mesh : extract_range_iter_looptri_bm;
+ stop = mr->tri_len;
+ break;
+ case MR_ITER_POLY:
+ range_data.elems = is_mesh ? mr->mpoly : (void *)mr->bm->ftable;
+ func = is_mesh ? extract_range_iter_poly_mesh : extract_range_iter_poly_bm;
+ stop = mr->poly_len;
+ break;
+ case MR_ITER_LEDGE:
+ range_data.loose_elems = mr->ledges;
+ range_data.elems = is_mesh ? mr->medge : (void *)mr->bm->etable;
+ func = is_mesh ? extract_range_iter_ledge_mesh : extract_range_iter_ledge_bm;
+ stop = mr->edge_loose_len;
+ break;
+ case MR_ITER_LVERT:
+ range_data.loose_elems = mr->lverts;
+ range_data.elems = is_mesh ? mr->mvert : (void *)mr->bm->vtable;
+ func = is_mesh ? extract_range_iter_lvert_mesh : extract_range_iter_lvert_bm;
+ stop = mr->vert_loose_len;
+ break;
+ default:
+ BLI_assert(false);
+ return;
+ }
+
+ extractors->filter_into(range_data.extractors, iter_type);
+ BLI_task_parallel_range(0, stop, &range_data, func, settings);
+}
+
+static void extract_task_range_run(void *__restrict taskdata)
+{
+ ExtractTaskData *data = (ExtractTaskData *)taskdata;
+ const eMRIterType iter_type = data->iter_type;
+ const bool is_mesh = data->mr->extract_type != MR_EXTRACT_BMESH;
+
+ size_t userdata_chunk_size = data->extractors->data_size_total();
+ char *userdata_chunk = new char[userdata_chunk_size];
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = data->use_threading;
+ settings.userdata_chunk = userdata_chunk;
+ settings.userdata_chunk_size = userdata_chunk_size;
+ settings.func_reduce = extract_task_reduce;
+ settings.min_iter_per_thread = MIM_RANGE_LEN;
+
+ extract_init(data->mr, data->cache, *data->extractors, data->mbc, (void *)userdata_chunk);
+
+ if (iter_type & MR_ITER_LOOPTRI) {
+ extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LOOPTRI, is_mesh, &settings);
+ }
+ if (iter_type & MR_ITER_POLY) {
+ extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_POLY, is_mesh, &settings);
+ }
+ if (iter_type & MR_ITER_LEDGE) {
+ extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LEDGE, is_mesh, &settings);
+ }
+ if (iter_type & MR_ITER_LVERT) {
+ extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LVERT, is_mesh, &settings);
+ }
+
+ extract_finish(data->mr, data->cache, *data->extractors, (void *)userdata_chunk);
+ delete[] userdata_chunk;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract In Parallel Ranges
+ * \{ */
+
+static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph,
+ const MeshRenderData *mr,
+ MeshBatchCache *cache,
+ ExtractorRunDatas *extractors,
+ MeshBufferCache *mbc,
+ const bool use_threading)
+{
+ ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, use_threading);
+ struct TaskNode *task_node = BLI_task_graph_node_create(
+ task_graph,
+ extract_task_range_run,
+ taskdata,
+ (TaskGraphNodeFreeFunction)extract_task_data_free);
+ return task_node;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Task Node - Update Mesh Render Data
+ * \{ */
+struct MeshRenderDataUpdateTaskData {
+ MeshRenderData *mr = nullptr;
+ eMRIterType iter_type;
+ eMRDataType data_flag;
+
+ MeshRenderDataUpdateTaskData(MeshRenderData *mr, eMRIterType iter_type, eMRDataType data_flag)
+ : mr(mr), iter_type(iter_type), data_flag(data_flag)
+ {
+ }
+
+ ~MeshRenderDataUpdateTaskData()
+ {
+ mesh_render_data_free(mr);
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData")
+#endif
+};
+
+static void mesh_render_data_update_task_data_free(void *data)
+{
+ MeshRenderDataUpdateTaskData *taskdata = static_cast<MeshRenderDataUpdateTaskData *>(data);
+ BLI_assert(taskdata);
+ delete taskdata;
+}
+
+static void mesh_extract_render_data_node_exec(void *__restrict task_data)
+{
+ MeshRenderDataUpdateTaskData *update_task_data = static_cast<MeshRenderDataUpdateTaskData *>(
+ task_data);
+ MeshRenderData *mr = update_task_data->mr;
+ const eMRIterType iter_type = update_task_data->iter_type;
+ const eMRDataType data_flag = update_task_data->data_flag;
+
+ mesh_render_data_update_normals(mr, data_flag);
+ mesh_render_data_update_looptris(mr, iter_type, data_flag);
+}
+
+static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
+ MeshRenderData *mr,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag)
+{
+ MeshRenderDataUpdateTaskData *task_data = new MeshRenderDataUpdateTaskData(
+ mr, iter_type, data_flag);
+
+ struct TaskNode *task_node = BLI_task_graph_node_create(
+ task_graph,
+ mesh_extract_render_data_node_exec,
+ task_data,
+ (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free);
+ return task_node;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loop
+ * \{ */
+
+static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
+ MeshBatchCache *cache,
+ MeshBufferCache *mbc,
+ MeshBufferExtractionCache *extraction_cache,
+ Mesh *me,
+
+ const bool is_editmode,
+ const bool is_paint_mode,
+ const bool is_mode_active,
+ const float obmat[4][4],
+ const bool do_final,
+ const bool do_uvedit,
+ const bool use_subsurf_fdots,
+ const Scene *scene,
+ const ToolSettings *ts,
+ const bool use_hide)
+{
+ /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph.
+ * This sub-graph starts with an extract_render_data_node. This fills/converts the required
+ * data from Mesh.
+ *
+ * Small extractions and extractions that can't be multi-threaded are grouped in a single
+ * `extract_single_threaded_task_node`.
+ *
+ * Other extractions will create a node for each loop exceeding 8192 items. these nodes are
+ * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the
+ * user_data needed for the extraction based on the data extracted from the mesh.
+ * counters are used to check if the finalize of a task has to be called.
+ *
+ * Mesh extraction sub graph
+ *
+ * +----------------------+
+ * +-----> | extract_task1_loop_1 |
+ * | +----------------------+
+ * +------------------+ +----------------------+ +----------------------+
+ * | mesh_render_data | --> | | --> | extract_task1_loop_2 |
+ * +------------------+ | | +----------------------+
+ * | | | +----------------------+
+ * | | user_data_init | --> | extract_task2_loop_1 |
+ * v | | +----------------------+
+ * +------------------+ | | +----------------------+
+ * | single_threaded | | | --> | extract_task2_loop_2 |
+ * +------------------+ +----------------------+ +----------------------+
+ * | +----------------------+
+ * +-----> | extract_task2_loop_3 |
+ * +----------------------+
+ */
+ const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
+ GPU_use_hq_normals_workaround();
+ const bool override_single_mat = mesh_render_mat_len_get(me) <= 1;
+
+ /* Create an array containing all the extractors that needs to be executed. */
+ ExtractorRunDatas extractors;
+
+#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \
+ do { \
+ if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \
+ const MeshExtract *extractor = mesh_extract_override_get( \
+ &extract_##name, do_hq_normals, override_single_mat); \
+ extractors.append(extractor); \
+ } \
+ } while (0)
+
+ EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, lnor);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, uv);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, tan);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, vcol);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, orco);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, weights);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots);
+
+ EXTRACT_ADD_REQUESTED(IBO, ibo, tris);
+ if (DRW_ibo_requested(mbc->ibo.lines)) {
+ const MeshExtract *extractor;
+ if (mbc->ibo.lines_loose != nullptr) {
+ /* Update #lines_loose ibo. */
+ extractor = &extract_lines_with_lines_loose;
+ }
+ else {
+ extractor = &extract_lines;
+ }
+ extractors.append(extractor);
+ }
+ else if (DRW_ibo_requested(mbc->ibo.lines_loose)) {
+ /* Note: #ibo.lines must have been created first. */
+ const MeshExtract *extractor = &extract_lines_loose_only;
+ extractors.append(extractor);
+ }
+ EXTRACT_ADD_REQUESTED(IBO, ibo, points);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, fdots);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots);
+
+#undef EXTRACT_ADD_REQUESTED
+
+ if (extractors.is_empty()) {
+ return;
+ }
+
+#ifdef DEBUG_TIME
+ double rdata_start = PIL_check_seconds_timer();
+#endif
+
+ eMRIterType iter_type = extractors.iter_types();
+ eMRDataType data_flag = extractors.data_types();
+
+ MeshRenderData *mr = mesh_render_data_create(me,
+ extraction_cache,
+ is_editmode,
+ is_paint_mode,
+ is_mode_active,
+ obmat,
+ do_final,
+ do_uvedit,
+ ts,
+ iter_type);
+ mr->use_hide = use_hide;
+ mr->use_subsurf_fdots = use_subsurf_fdots;
+ mr->use_final_mesh = do_final;
+
+#ifdef DEBUG_TIME
+ double rdata_end = PIL_check_seconds_timer();
+#endif
+
+ struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
+ task_graph, mr, iter_type, data_flag);
+
+ /* Simple heuristic. */
+ const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIM_RANGE_LEN;
+
+ if (use_thread) {
+ /* First run the requested extractors that do not support asynchronous ranges. */
+ for (const ExtractorRunData &run_data : extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ if (!extractor->use_threading) {
+ ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas();
+ single_threaded_extractors->append(extractor);
+ struct TaskNode *task_node = extract_task_node_create(
+ task_graph, mr, cache, single_threaded_extractors, mbc, false);
+
+ BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
+ }
+ }
+
+ /* Distribute the remaining extractors into ranges per core. */
+ ExtractorRunDatas *multi_threaded_extractors = new ExtractorRunDatas();
+ extractors.filter_threaded_extractors_into(*multi_threaded_extractors);
+ if (!multi_threaded_extractors->is_empty()) {
+ struct TaskNode *task_node = extract_task_node_create(
+ task_graph, mr, cache, multi_threaded_extractors, mbc, true);
+
+ BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
+ }
+ else {
+ /* No tasks created freeing extractors list. */
+ delete multi_threaded_extractors;
+ }
+ }
+ else {
+ /* Run all requests on the same thread. */
+ ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors);
+ struct TaskNode *task_node = extract_task_node_create(
+ task_graph, mr, cache, extractors_copy, mbc, false);
+
+ BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
+ }
+
+ /* Trigger the sub-graph for this mesh. */
+ BLI_task_graph_node_push_work(task_node_mesh_render_data);
+
+#ifdef DEBUG_TIME
+ BLI_task_graph_work_and_wait(task_graph);
+ double end = PIL_check_seconds_timer();
+
+ static double avg = 0;
+ static double avg_fps = 0;
+ static double avg_rdata = 0;
+ static double end_prev = 0;
+
+ if (end_prev == 0) {
+ end_prev = end;
+ }
+
+ avg = avg * 0.95 + (end - rdata_end) * 0.05;
+ avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05;
+ avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05;
+
+ printf(
+ "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000);
+
+ end_prev = end;
+#endif
+}
+
+} // namespace blender::draw
+
+extern "C" {
+void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
+ MeshBatchCache *cache,
+ MeshBufferCache *mbc,
+ MeshBufferExtractionCache *extraction_cache,
+ Mesh *me,
+
+ const bool is_editmode,
+ const bool is_paint_mode,
+ const bool is_mode_active,
+ const float obmat[4][4],
+ const bool do_final,
+ const bool do_uvedit,
+ const bool use_subsurf_fdots,
+ const Scene *scene,
+ const ToolSettings *ts,
+ const bool use_hide)
+{
+ blender::draw::mesh_buffer_cache_create_requested(task_graph,
+ cache,
+ mbc,
+ extraction_cache,
+ me,
+ is_editmode,
+ is_paint_mode,
+ is_mode_active,
+ obmat,
+ do_final,
+ do_uvedit,
+ use_subsurf_fdots,
+ scene,
+ ts,
+ use_hide);
+}
+
+} // extern "C"
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c
new file mode 100644
index 00000000000..3a8edcad463
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c
@@ -0,0 +1,3687 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "atomic_ops.h"
+
+#include "DNA_object_types.h"
+
+#include "BLI_edgehash.h"
+#include "BLI_jitter_2d.h"
+#include "BLI_kdopbvh.h"
+#include "BLI_string.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_deform.h"
+#include "BKE_editmesh.h"
+#include "BKE_editmesh_bvh.h"
+#include "BKE_editmesh_cache.h"
+#include "BKE_editmesh_tangent.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_tangent.h"
+#include "BKE_paint.h"
+
+#include "ED_uvedit.h"
+
+#include "GPU_capabilities.h"
+
+#include "draw_cache_extract_mesh_private.h"
+#include "draw_cache_impl.h"
+
+void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc)
+{
+ /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to
+ * `MeshBufferCache *`. What shows a different usage versus intent. */
+ void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset);
+ void *buffer = *buffer_ptr;
+ BLI_assert(buffer);
+ return buffer;
+}
+
+eMRIterType mesh_extract_iter_type(const MeshExtract *ext)
+{
+ eMRIterType type = 0;
+ SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI);
+ SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY);
+ SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE);
+ SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT);
+ return type;
+}
+
+/* ---------------------------------------------------------------------- */
+/** \name Override extractors
+ * Extractors can be overridden. When overridden a specialized version is used. The next functions
+ * would check for any needed overrides and usage of the specialized version.
+ * \{ */
+
+static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor)
+{
+ if (extractor == &extract_pos_nor) {
+ return &extract_pos_nor_hq;
+ }
+ if (extractor == &extract_lnor) {
+ return &extract_lnor_hq;
+ }
+ if (extractor == &extract_tan) {
+ return &extract_tan_hq;
+ }
+ if (extractor == &extract_fdots_nor) {
+ return &extract_fdots_nor_hq;
+ }
+ return extractor;
+}
+
+static const MeshExtract *mesh_extract_override_single_material(const MeshExtract *extractor)
+{
+ if (extractor == &extract_tris) {
+ return &extract_tris_single_mat;
+ }
+ return extractor;
+}
+
+const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
+ const bool do_hq_normals,
+ const bool do_single_mat)
+{
+ if (do_hq_normals) {
+ extractor = mesh_extract_override_hq_normals(extractor);
+ }
+
+ if (do_single_mat) {
+ extractor = mesh_extract_override_single_material(extractor);
+ }
+
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Position and Vertex Normal
+ * \{ */
+
+typedef struct PosNorLoop {
+ float pos[3];
+ GPUPackedNormal nor;
+} PosNorLoop;
+
+typedef struct MeshExtract_PosNor_Data {
+ PosNorLoop *vbo_data;
+ GPUNormal *normals;
+} MeshExtract_PosNor_Data;
+
+static void extract_pos_nor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* WARNING Adjust #PosNorLoop struct accordingly. */
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_alias_add(&format, "vnor");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+ /* Pack normals per vert, reduce amount of computation. */
+ MeshExtract_PosNor_Data *data = tls_data;
+ data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo);
+ data->normals = MEM_mallocN(sizeof(GPUNormal) * mr->vert_len, __func__);
+
+ /* Quicker than doing it for each loop. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMVert *eve;
+ int v;
+ BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
+ data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve));
+ }
+ }
+ else {
+ const MVert *mv = mr->mvert;
+ for (int v = 0; v < mr->vert_len; v++, mv++) {
+ data->normals[v].low = GPU_normal_convert_i10_s3(mv->no);
+ }
+ }
+}
+
+static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v));
+ vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low;
+ vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0;
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ const MVert *mv = &mr->mvert[ml->v];
+ copy_v3_v3(vert->pos, mv->co);
+ vert->nor = data->normals[ml->v].low;
+ /* Flag for paint mode overlay. */
+ if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
+ (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
+ vert->nor.w = -1;
+ }
+ else if (mv->flag & SELECT) {
+ vert->nor.w = 1;
+ }
+ else {
+ vert->nor.w = 0;
+ }
+ }
+}
+
+static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+
+ int l_index = mr->loop_len + ledge_index * 2;
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
+ copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
+ vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low;
+ vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low;
+}
+
+static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ const int ml_index = mr->loop_len + ledge_index * 2;
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
+ copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
+ vert[0].nor = data->normals[med->v1].low;
+ vert[1].nor = data->normals[med->v2].low;
+}
+
+static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int l_index = offset + lvert_index;
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
+ vert->nor = data->normals[BM_elem_index_get(eve)].low;
+}
+
+static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *mv,
+ const int lvert_index,
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int ml_index = offset + lvert_index;
+ const int v_index = mr->lverts[lvert_index];
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert->pos, mv->co);
+ vert->nor = data->normals[v_index].low;
+}
+
+static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ MEM_freeN(data->normals);
+}
+
+const MeshExtract extract_pos_nor = {
+ .init = extract_pos_nor_init,
+ .iter_poly_bm = extract_pos_nor_iter_poly_bm,
+ .iter_poly_mesh = extract_pos_nor_iter_poly_mesh,
+ .iter_ledge_bm = extract_pos_nor_iter_ledge_bm,
+ .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh,
+ .iter_lvert_bm = extract_pos_nor_iter_lvert_bm,
+ .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh,
+ .finish = extract_pos_nor_finish,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_PosNor_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Position and High Quality Vertex Normal
+ * \{ */
+
+typedef struct PosNorHQLoop {
+ float pos[3];
+ short nor[4];
+} PosNorHQLoop;
+
+typedef struct MeshExtract_PosNorHQ_Data {
+ PosNorHQLoop *vbo_data;
+ GPUNormal *normals;
+} MeshExtract_PosNorHQ_Data;
+
+static void extract_pos_nor_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* WARNING Adjust #PosNorHQLoop struct accordingly. */
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_alias_add(&format, "vnor");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+ /* Pack normals per vert, reduce amount of computation. */
+ MeshExtract_PosNorHQ_Data *data = tls_data;
+ data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo);
+ data->normals = MEM_mallocN(sizeof(GPUNormal) * mr->vert_len, __func__);
+
+ /* Quicker than doing it for each loop. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMVert *eve;
+ int v;
+ BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
+ normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve));
+ }
+ }
+ else {
+ const MVert *mv = mr->mvert;
+ for (int v = 0; v < mr->vert_len; v++, mv++) {
+ copy_v3_v3_short(data->normals[v].high, mv->no);
+ }
+ }
+}
+
+static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ PosNorHQLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v));
+ copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high);
+
+ BMFace *efa = l_iter->f;
+ vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ PosNorHQLoop *vert = &data->vbo_data[ml_index];
+ const MVert *mv = &mr->mvert[ml->v];
+ copy_v3_v3(vert->pos, mv->co);
+ copy_v3_v3_short(vert->nor, data->normals[ml->v].high);
+
+ /* Flag for paint mode overlay. */
+ if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
+ (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
+ vert->nor[3] = -1;
+ }
+ else if (mv->flag & SELECT) {
+ vert->nor[3] = 1;
+ }
+ else {
+ vert->nor[3] = 0;
+ }
+ }
+}
+
+static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ int l_index = mr->loop_len + ledge_index * 2;
+ PosNorHQLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
+ copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
+ copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high);
+ vert[0].nor[3] = 0;
+ copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high);
+ vert[1].nor[3] = 0;
+}
+
+static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ const int ml_index = mr->loop_len + ledge_index * 2;
+ PosNorHQLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
+ copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
+ copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high);
+ vert[0].nor[3] = 0;
+ copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high);
+ vert[1].nor[3] = 0;
+}
+
+static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int l_index = offset + lvert_index;
+ PosNorHQLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
+ copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high);
+ vert->nor[3] = 0;
+}
+
+static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *mv,
+ const int lvert_index,
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int ml_index = offset + lvert_index;
+ const int v_index = mr->lverts[lvert_index];
+ PosNorHQLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert->pos, mv->co);
+ copy_v3_v3_short(vert->nor, data->normals[v_index].high);
+ vert->nor[3] = 0;
+}
+
+static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ MEM_freeN(data->normals);
+}
+
+const MeshExtract extract_pos_nor_hq = {
+ .init = extract_pos_nor_hq_init,
+ .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm,
+ .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh,
+ .iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm,
+ .iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh,
+ .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm,
+ .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh,
+ .finish = extract_pos_nor_hq_finish,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_PosNorHQ_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)};
+
+/** \} */
+/* ---------------------------------------------------------------------- */
+/** \name Extract HQ Loop Normal
+ * \{ */
+
+typedef struct gpuHQNor {
+ short x, y, z, w;
+} gpuHQNor;
+
+static void extract_lnor_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_alias_add(&format, "lnor");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ *(gpuHQNor **)tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ if (mr->loop_normals) {
+ normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, mr->loop_normals[l_index]);
+ }
+ else {
+ if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
+ normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, bm_vert_no_get(mr, l_iter->v));
+ }
+ else {
+ normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, bm_face_no_get(mr, f));
+ }
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ gpuHQNor *lnor_data = &(*(gpuHQNor **)data)[ml_index];
+ if (mr->loop_normals) {
+ normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]);
+ }
+ else if (mp->flag & ME_SMOOTH) {
+ copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no);
+ }
+ else {
+ normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]);
+ }
+
+ /* Flag for paint mode overlay.
+ * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
+ * In paint mode it will use the un-mapped data to draw the wire-frame. */
+ if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
+ (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
+ lnor_data->w = -1;
+ }
+ else if (mp->flag & ME_FACE_SEL) {
+ lnor_data->w = 1;
+ }
+ else {
+ lnor_data->w = 0;
+ }
+ }
+}
+
+const MeshExtract extract_lnor_hq = {
+ .init = extract_lnor_hq_init,
+ .iter_poly_bm = extract_lnor_hq_iter_poly_bm,
+ .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh,
+ .data_type = MR_DATA_LOOP_NOR,
+ .data_size = sizeof(gpuHQNor *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor),
+};
+
+/** \} */
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loop Normal
+ * \{ */
+
+static void extract_lnor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_alias_add(&format, "lnor");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ *(GPUPackedNormal **)tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_lnor_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ if (mr->loop_normals) {
+ (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]);
+ }
+ else {
+ if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
+ (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3(
+ bm_vert_no_get(mr, l_iter->v));
+ }
+ else {
+ (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f));
+ }
+ }
+ (*(GPUPackedNormal **)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0;
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ GPUPackedNormal *lnor_data = &(*(GPUPackedNormal **)data)[ml_index];
+ if (mr->loop_normals) {
+ *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]);
+ }
+ else if (mp->flag & ME_SMOOTH) {
+ *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no);
+ }
+ else {
+ *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]);
+ }
+
+ /* Flag for paint mode overlay.
+ * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
+ * In paint mode it will use the un-mapped data to draw the wire-frame. */
+ if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
+ (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
+ lnor_data->w = -1;
+ }
+ else if (mp->flag & ME_FACE_SEL) {
+ lnor_data->w = 1;
+ }
+ else {
+ lnor_data->w = 0;
+ }
+ }
+}
+
+const MeshExtract extract_lnor = {
+ .init = extract_lnor_init,
+ .iter_poly_bm = extract_lnor_iter_poly_bm,
+ .iter_poly_mesh = extract_lnor_iter_poly_mesh,
+ .data_type = MR_DATA_LOOP_NOR,
+ .data_size = sizeof(GPUPackedNormal *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract UV layers
+ * \{ */
+
+static void extract_uv_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ GPUVertFormat format = {0};
+ GPU_vertformat_deinterleave(&format);
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ uint32_t uv_layers = cache->cd_used.uv;
+ /* HACK to fix T68857 */
+ if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) {
+ int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
+ if (layer != -1) {
+ uv_layers |= (1 << layer);
+ }
+ }
+
+ for (int i = 0; i < MAX_MTFACE; i++) {
+ if (uv_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
+
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+ /* UV layer name. */
+ BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Auto layer name. */
+ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+ GPU_vertformat_alias_add(&format, attr_name);
+ /* Active render layer name. */
+ if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "u");
+ }
+ /* Active display layer name. */
+ if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "au");
+ /* Alias to `pos` for edit uvs. */
+ GPU_vertformat_alias_add(&format, "pos");
+ }
+ /* Stencil mask uv layer name. */
+ if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "mu");
+ }
+ }
+ }
+
+ int v_len = mr->loop_len;
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ /* VBO will not be used, only allocate minimum of memory. */
+ v_len = 1;
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, v_len);
+
+ float(*uv_data)[2] = (float(*)[2])GPU_vertbuf_get_data(vbo);
+ for (int i = 0; i < MAX_MTFACE; i++) {
+ if (uv_layers & (1 << i)) {
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
+ memcpy(uv_data, luv->uv, sizeof(*uv_data));
+ uv_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) {
+ memcpy(uv_data, layer_data->uv, sizeof(*uv_data));
+ }
+ }
+ }
+ }
+}
+
+const MeshExtract extract_uv = {
+ .init = extract_uv_init,
+ .data_type = 0,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Tangent layers
+ * \{ */
+
+static void extract_tan_ex_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ GPUVertBuf *vbo,
+ const bool do_hq)
+{
+ GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10;
+ GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT;
+
+ GPUVertFormat format = {0};
+ GPU_vertformat_deinterleave(&format);
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
+ uint32_t tan_layers = cache->cd_used.tan;
+ float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO);
+ bool orco_allocated = false;
+ const bool use_orco_tan = cache->cd_used.tan_orco != 0;
+
+ int tan_len = 0;
+ char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
+
+ for (int i = 0; i < MAX_MTFACE; i++) {
+ if (tan_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+ /* Tangent layer name. */
+ BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
+ /* Active render layer name. */
+ if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "t");
+ }
+ /* Active display layer name. */
+ if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "at");
+ }
+
+ BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME);
+ }
+ }
+ if (use_orco_tan && orco == NULL) {
+ /* If `orco` is not available compute it ourselves */
+ orco_allocated = true;
+ orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMesh *bm = mr->bm;
+ for (int v = 0; v < mr->vert_len; v++) {
+ const BMVert *eve = BM_vert_at_index(bm, v);
+ /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords.
+ * not the distorted ones. */
+ copy_v3_v3(orco[v], eve->co);
+ }
+ }
+ else {
+ const MVert *mv = mr->mvert;
+ for (int v = 0; v < mr->vert_len; v++, mv++) {
+ copy_v3_v3(orco[v], mv->co);
+ }
+ }
+ BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0);
+ }
+
+ /* Start Fresh */
+ CustomData loop_data;
+ CustomData_reset(&loop_data);
+ if (tan_len != 0 || use_orco_tan) {
+ short tangent_mask = 0;
+ bool calc_active_tangent = false;
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BKE_editmesh_loop_tangent_calc(mr->edit_bmesh,
+ calc_active_tangent,
+ tangent_names,
+ tan_len,
+ mr->poly_normals,
+ mr->loop_normals,
+ orco,
+ &loop_data,
+ mr->loop_len,
+ &tangent_mask);
+ }
+ else {
+ BKE_mesh_calc_loop_tangent_ex(mr->mvert,
+ mr->mpoly,
+ mr->poly_len,
+ mr->mloop,
+ mr->mlooptri,
+ mr->tri_len,
+ cd_ldata,
+ calc_active_tangent,
+ tangent_names,
+ tan_len,
+ mr->poly_normals,
+ mr->loop_normals,
+ orco,
+ &loop_data,
+ mr->loop_len,
+ &tangent_mask);
+ }
+ }
+
+ if (use_orco_tan) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+ BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
+ GPU_vertformat_alias_add(&format, "t");
+ GPU_vertformat_alias_add(&format, "at");
+ }
+
+ if (orco_allocated) {
+ MEM_SAFE_FREE(orco);
+ }
+
+ int v_len = mr->loop_len;
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ /* VBO will not be used, only allocate minimum of memory. */
+ v_len = 1;
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, v_len);
+
+ if (do_hq) {
+ short(*tan_data)[4] = (short(*)[4])GPU_vertbuf_get_data(vbo);
+ for (int i = 0; i < tan_len; i++) {
+ const char *name = tangent_names[i];
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(
+ &loop_data, CD_TANGENT, name);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
+ (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
+ tan_data++;
+ }
+ }
+ if (use_orco_tan) {
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
+ (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
+ tan_data++;
+ }
+ }
+ }
+ else {
+ GPUPackedNormal *tan_data = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
+ for (int i = 0; i < tan_len; i++) {
+ const char *name = tangent_names[i];
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(
+ &loop_data, CD_TANGENT, name);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
+ tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
+ tan_data++;
+ }
+ }
+ if (use_orco_tan) {
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
+ tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
+ tan_data++;
+ }
+ }
+ }
+
+ CustomData_free(&loop_data, mr->loop_len);
+}
+
+static void extract_tan_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ extract_tan_ex_init(mr, cache, buf, false);
+}
+
+const MeshExtract extract_tan = {
+ .init = extract_tan_init,
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract HQ Tangent layers
+ * \{ */
+
+static void extract_tan_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ extract_tan_ex_init(mr, cache, buf, true);
+}
+
+const MeshExtract extract_tan_hq = {
+ .init = extract_tan_hq_init,
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+ .data_size = 0,
+ .use_threading = false,
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Sculpt Data
+ * \{ */
+
+static void extract_sculpt_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ GPUVertFormat format = {0};
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
+ CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata;
+
+ float *cd_mask = CustomData_get_layer(cd_vdata, CD_PAINT_MASK);
+ int *cd_face_set = CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS);
+
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "fset", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ typedef struct gpuSculptData {
+ uint8_t face_set_color[4];
+ float mask;
+ } gpuSculptData;
+
+ gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo);
+ MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK);
+ int cd_face_set_ofs = CustomData_get_offset(cd_pdata, CD_SCULPT_FACE_SETS);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ float v_mask = 0.0f;
+ if (cd_mask) {
+ v_mask = BM_ELEM_CD_GET_FLOAT(l_iter->v, cd_mask_ofs);
+ }
+ vbo_data->mask = v_mask;
+ uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
+ if (cd_face_set) {
+ const int face_set_id = BM_ELEM_CD_GET_INT(l_iter->f, cd_face_set_ofs);
+ if (face_set_id != mr->me->face_sets_color_default) {
+ BKE_paint_face_set_overlay_color_get(
+ face_set_id, mr->me->face_sets_color_seed, face_set_color);
+ }
+ }
+ copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color);
+ vbo_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ int mp_loop = 0;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
+ const MPoly *p = &mr->mpoly[mp_index];
+ for (int l = 0; l < p->totloop; l++) {
+ float v_mask = 0.0f;
+ if (cd_mask) {
+ v_mask = cd_mask[loops[mp_loop].v];
+ }
+ vbo_data->mask = v_mask;
+
+ uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
+ if (cd_face_set) {
+ const int face_set_id = cd_face_set[mp_index];
+ /* Skip for the default color Face Set to render it white. */
+ if (face_set_id != mr->me->face_sets_color_default) {
+ BKE_paint_face_set_overlay_color_get(
+ face_set_id, mr->me->face_sets_color_seed, face_set_color);
+ }
+ }
+ copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color);
+ mp_loop++;
+ vbo_data++;
+ }
+ }
+ }
+}
+
+const MeshExtract extract_sculpt_data = {
+ .init = extract_sculpt_data_init,
+ .data_type = 0,
+ .data_size = 0,
+ /* TODO: enable threading. */
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract VCol
+ * \{ */
+
+static void extract_vcol_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ GPUVertFormat format = {0};
+ GPU_vertformat_deinterleave(&format);
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
+ uint32_t vcol_layers = cache->cd_used.vcol;
+ uint32_t svcol_layers = cache->cd_used.sculpt_vcol;
+
+ for (int i = 0; i < MAX_MCOL; i++) {
+ if (vcol_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+
+ BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+ if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) {
+ GPU_vertformat_alias_add(&format, "c");
+ }
+ if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) {
+ GPU_vertformat_alias_add(&format, "ac");
+ }
+
+ /* Gather number of auto layers. */
+ /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */
+ if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 &&
+ CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) {
+ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+ GPU_vertformat_alias_add(&format, attr_name);
+ }
+ }
+ }
+
+ /* Sculpt Vertex Colors */
+ if (U.experimental.use_sculpt_vertex_colors) {
+ for (int i = 0; i < 8; i++) {
+ if (svcol_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+
+ BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+ if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) {
+ GPU_vertformat_alias_add(&format, "c");
+ }
+ if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) {
+ GPU_vertformat_alias_add(&format, "ac");
+ }
+ /* Gather number of auto layers. */
+ /* We only do `vcols` that are not overridden by `uvs`. */
+ if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
+ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+ GPU_vertformat_alias_add(&format, attr_name);
+ }
+ }
+ }
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ typedef struct gpuMeshVcol {
+ ushort r, g, b, a;
+ } gpuMeshVcol;
+
+ gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo);
+ MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
+
+ for (int i = 0; i < MAX_MCOL; i++) {
+ if (vcol_layers & (1 << i)) {
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
+ vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
+ vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
+ vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
+ vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
+ vcol_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) {
+ vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
+ vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
+ vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
+ vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
+ }
+ }
+ }
+
+ if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) {
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs);
+ vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]);
+ vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]);
+ vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]);
+ vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]);
+ vcol_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) {
+ vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]);
+ vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]);
+ vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]);
+ vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]);
+ }
+ }
+ }
+ }
+}
+
+const MeshExtract extract_vcol = {
+ .init = extract_vcol_init,
+ .data_type = 0,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Orco
+ * \{ */
+
+typedef struct MeshExtract_Orco_Data {
+ float (*vbo_data)[4];
+ float (*orco)[3];
+} MeshExtract_Orco_Data;
+
+static void extract_orco_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
+ * attributes. This is a substantial waste of video-ram and should be done another way.
+ * Unfortunately, at the time of writing, I did not found any other "non disruptive"
+ * alternative. */
+ GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ CustomData *cd_vdata = &mr->me->vdata;
+
+ MeshExtract_Orco_Data *data = tls_data;
+ data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo);
+ data->orco = CustomData_get_layer(cd_vdata, CD_ORCO);
+ /* Make sure `orco` layer was requested only if needed! */
+ BLI_assert(data->orco);
+}
+
+static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ float *loop_orco = orco_data->vbo_data[l_index];
+ copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
+ float *loop_orco = orco_data->vbo_data[ml_index];
+ copy_v3_v3(loop_orco, orco_data->orco[ml->v]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ }
+}
+
+const MeshExtract extract_orco = {
+ .init = extract_orco_init,
+ .iter_poly_bm = extract_orco_iter_poly_bm,
+ .iter_poly_mesh = extract_orco_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_Orco_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edge Factor
+ * Defines how much an edge is visible.
+ * \{ */
+
+typedef struct MeshExtract_EdgeFac_Data {
+ uchar *vbo_data;
+ bool use_edge_render;
+ /* Number of loop per edge. */
+ uchar *edge_loop_count;
+} MeshExtract_EdgeFac_Data;
+
+static float loop_edge_factor_get(const float f_no[3],
+ const float v_co[3],
+ const float v_no[3],
+ const float v_next_co[3])
+{
+ float enor[3], evec[3];
+ sub_v3_v3v3(evec, v_next_co, v_co);
+ cross_v3_v3v3(enor, v_no, evec);
+ normalize_v3(enor);
+ float d = fabsf(dot_v3v3(enor, f_no));
+ /* Re-scale to the slider range. */
+ d *= (1.0f / 0.065f);
+ CLAMP(d, 0.0f, 1.0f);
+ return d;
+}
+
+static void extract_edge_fac_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+ MeshExtract_EdgeFac_Data *data = tls_data;
+
+ if (mr->extract_type == MR_EXTRACT_MESH) {
+ data->edge_loop_count = MEM_callocN(sizeof(uint32_t) * mr->edge_len, __func__);
+
+ /* HACK(fclem) Detecting the need for edge render.
+ * We could have a flag in the mesh instead or check the modifier stack. */
+ const MEdge *med = mr->medge;
+ for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) {
+ if ((med->flag & ME_EDGERENDER) == 0) {
+ data->use_edge_render = true;
+ break;
+ }
+ }
+ }
+ else {
+ /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */
+ data->use_edge_render = true;
+ }
+
+ data->vbo_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_EdgeFac_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ if (BM_edge_is_manifold(l_iter->e)) {
+ float ratio = loop_edge_factor_get(bm_face_no_get(mr, f),
+ bm_vert_co_get(mr, l_iter->v),
+ bm_vert_no_get(mr, l_iter->v),
+ bm_vert_co_get(mr, l_iter->next->v));
+ data->vbo_data[l_index] = ratio * 253 + 1;
+ }
+ else {
+ data->vbo_data[l_index] = 255;
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ if (data->use_edge_render) {
+ const MEdge *med = &mr->medge[ml->e];
+ data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0;
+ }
+ else {
+
+ /* Count loop per edge to detect non-manifold. */
+ if (data->edge_loop_count[ml->e] < 3) {
+ data->edge_loop_count[ml->e]++;
+ }
+ if (data->edge_loop_count[ml->e] == 2) {
+ /* Manifold */
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ const MLoop *ml_next = &mr->mloop[ml_index_other];
+ const MVert *v1 = &mr->mvert[ml->v];
+ const MVert *v2 = &mr->mvert[ml_next->v];
+ float vnor_f[3];
+ normal_short_to_float_v3(vnor_f, v1->no);
+ float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co);
+ data->vbo_data[ml_index] = ratio * 253 + 1;
+ }
+ else {
+ /* Non-manifold */
+ data->vbo_data[ml_index] = 255;
+ }
+ }
+ }
+}
+
+static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *UNUSED(eed),
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_EdgeFac_Data *data = _data;
+ data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255;
+ data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255;
+}
+
+static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *UNUSED(med),
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_EdgeFac_Data *data = _data;
+
+ data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255;
+ data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255;
+}
+
+static void extract_edge_fac_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ GPUVertBuf *vbo = buf;
+ MeshExtract_EdgeFac_Data *data = _data;
+
+ if (GPU_crappy_amd_driver()) {
+ /* Some AMD drivers strangely crash with VBO's with a one byte format.
+ * To workaround we reinitialize the VBO with another format and convert
+ * all bytes to floats. */
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+ /* We keep the data reference in data->vbo_data. */
+ data->vbo_data = GPU_vertbuf_steal_data(vbo);
+ GPU_vertbuf_clear(vbo);
+
+ int buf_len = mr->loop_len + mr->loop_loose_len;
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, buf_len);
+
+ float *fdata = (float *)GPU_vertbuf_get_data(vbo);
+ for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) {
+ *fdata = data->vbo_data[ml_index] / 255.0f;
+ }
+ /* Free old byte data. */
+ MEM_freeN(data->vbo_data);
+ }
+ MEM_SAFE_FREE(data->edge_loop_count);
+}
+
+const MeshExtract extract_edge_fac = {
+ .init = extract_edge_fac_init,
+ .iter_poly_bm = extract_edge_fac_iter_poly_bm,
+ .iter_poly_mesh = extract_edge_fac_iter_poly_mesh,
+ .iter_ledge_bm = extract_edge_fac_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh,
+ .finish = extract_edge_fac_finish,
+ .data_type = MR_DATA_POLY_NOR,
+ .data_size = sizeof(MeshExtract_EdgeFac_Data),
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)};
+
+/** \} */
+/* ---------------------------------------------------------------------- */
+/** \name Extract Vertex Weight
+ * \{ */
+
+typedef struct MeshExtract_Weight_Data {
+ float *vbo_data;
+ const DRW_MeshWeightState *wstate;
+ const MDeformVert *dvert; /* For #Mesh. */
+ int cd_ofs; /* For #BMesh. */
+} MeshExtract_Weight_Data;
+
+static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate)
+{
+ /* Error state. */
+ if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) {
+ return -2.0f;
+ }
+ if (dvert == NULL) {
+ return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f;
+ }
+
+ float input = 0.0f;
+ if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
+ /* Multi-Paint feature */
+ bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE |
+ DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE));
+ input = BKE_defvert_multipaint_collective_weight(dvert,
+ wstate->defgroup_len,
+ wstate->defgroup_sel,
+ wstate->defgroup_sel_count,
+ is_normalized);
+ /* make it black if the selected groups have no weight on a vertex */
+ if (input == 0.0f) {
+ return -1.0f;
+ }
+ }
+ else {
+ /* default, non tricky behavior */
+ input = BKE_defvert_find_weight(dvert, wstate->defgroup_active);
+
+ if (input == 0.0f) {
+ switch (wstate->alert_mode) {
+ case OB_DRAW_GROUPUSER_ACTIVE:
+ return -1.0f;
+ break;
+ case OB_DRAW_GROUPUSER_ALL:
+ if (BKE_defvert_is_weight_zero(dvert, wstate->defgroup_len)) {
+ return -1.0f;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Lock-Relative: display the fraction of current weight vs total unlocked weight. */
+ if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) {
+ input = BKE_defvert_lock_relative_weight(
+ input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked);
+ }
+
+ CLAMP(input, 0.0f, 1.0f);
+ return input;
+}
+
+static void extract_weights_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+ MeshExtract_Weight_Data *data = tls_data;
+ data->vbo_data = (float *)GPU_vertbuf_get_data(vbo);
+ data->wstate = &cache->weight_state;
+
+ if (data->wstate->defgroup_active == -1) {
+ /* Nothing to show. */
+ data->dvert = NULL;
+ data->cd_ofs = -1;
+ }
+ else if (mr->extract_type == MR_EXTRACT_BMESH) {
+ data->dvert = NULL;
+ data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT);
+ }
+ else {
+ data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT);
+ data->cd_ofs = -1;
+ }
+}
+
+static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_Weight_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ if (data->cd_ofs != -1) {
+ const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs);
+ data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ else {
+ data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate);
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_weights_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_Weight_Data *data = _data;
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ if (data->dvert != NULL) {
+ const MDeformVert *dvert = &data->dvert[ml->v];
+ data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ else {
+ const MDeformVert *dvert = NULL;
+ data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ }
+}
+
+const MeshExtract extract_weights = {
+ .init = extract_weights_init,
+ .iter_poly_bm = extract_weights_iter_poly_bm,
+ .iter_poly_mesh = extract_weights_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_Weight_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit Mode Data / Flags
+ * \{ */
+
+typedef struct EditLoopData {
+ uchar v_flag;
+ uchar e_flag;
+ uchar crease;
+ uchar bweight;
+} EditLoopData;
+
+static void mesh_render_data_face_flag(const MeshRenderData *mr,
+ const BMFace *efa,
+ const int cd_ofs,
+ EditLoopData *eattr)
+{
+ if (efa == mr->efa_act) {
+ eattr->v_flag |= VFLAG_FACE_ACTIVE;
+ }
+ if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ eattr->v_flag |= VFLAG_FACE_SELECTED;
+ }
+
+ if (efa == mr->efa_act_uv) {
+ eattr->v_flag |= VFLAG_FACE_UV_ACTIVE;
+ }
+ if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) {
+ eattr->v_flag |= VFLAG_FACE_UV_SELECT;
+ }
+
+#ifdef WITH_FREESTYLE
+ if (mr->freestyle_face_ofs != -1) {
+ const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs);
+ if (ffa->flag & FREESTYLE_FACE_MARK) {
+ eattr->v_flag |= VFLAG_FACE_FREESTYLE;
+ }
+ }
+#endif
+}
+
+static void mesh_render_data_edge_flag(const MeshRenderData *mr,
+ const BMEdge *eed,
+ EditLoopData *eattr)
+{
+ const ToolSettings *ts = mr->toolsettings;
+ const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0;
+ const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE);
+
+ if (eed == mr->eed_act) {
+ eattr->e_flag |= VFLAG_EDGE_ACTIVE;
+ }
+ if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+ eattr->e_flag |= VFLAG_EDGE_SELECTED;
+ }
+ if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) &&
+ BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) {
+ eattr->e_flag |= VFLAG_EDGE_SELECTED;
+ eattr->e_flag |= VFLAG_VERT_SELECTED;
+ }
+ if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
+ eattr->e_flag |= VFLAG_EDGE_SEAM;
+ }
+ if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) {
+ eattr->e_flag |= VFLAG_EDGE_SHARP;
+ }
+
+ /* Use active edge color for active face edges because
+ * specular highlights make it hard to see T55456#510873.
+ *
+ * This isn't ideal since it can't be used when mixing edge/face modes
+ * but it's still better than not being able to see the active face. */
+ if (is_face_only_select_mode) {
+ if (mr->efa_act != NULL) {
+ if (BM_edge_in_face(eed, mr->efa_act)) {
+ eattr->e_flag |= VFLAG_EDGE_ACTIVE;
+ }
+ }
+ }
+
+ /* Use a byte for value range */
+ if (mr->crease_ofs != -1) {
+ float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs);
+ if (crease > 0) {
+ eattr->crease = (uchar)(crease * 255.0f);
+ }
+ }
+ /* Use a byte for value range */
+ if (mr->bweight_ofs != -1) {
+ float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs);
+ if (bweight > 0) {
+ eattr->bweight = (uchar)(bweight * 255.0f);
+ }
+ }
+#ifdef WITH_FREESTYLE
+ if (mr->freestyle_edge_ofs != -1) {
+ const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs);
+ if (fed->flag & FREESTYLE_EDGE_MARK) {
+ eattr->e_flag |= VFLAG_EDGE_FREESTYLE;
+ }
+ }
+#endif
+}
+
+static void mesh_render_data_loop_flag(const MeshRenderData *mr,
+ BMLoop *l,
+ const int cd_ofs,
+ EditLoopData *eattr)
+{
+ if (cd_ofs == -1) {
+ return;
+ }
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs);
+ if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) {
+ eattr->v_flag |= VFLAG_VERT_UV_PINNED;
+ }
+ if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) {
+ eattr->v_flag |= VFLAG_VERT_UV_SELECT;
+ }
+}
+
+static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr,
+ BMLoop *l,
+ const int cd_ofs,
+ EditLoopData *eattr)
+{
+ if (cd_ofs == -1) {
+ return;
+ }
+ if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) {
+ eattr->v_flag |= VFLAG_EDGE_UV_SELECT;
+ eattr->v_flag |= VFLAG_VERT_UV_SELECT;
+ }
+}
+
+static void mesh_render_data_vert_flag(const MeshRenderData *mr,
+ const BMVert *eve,
+ EditLoopData *eattr)
+{
+ if (eve == mr->eve_act) {
+ eattr->e_flag |= VFLAG_VERT_ACTIVE;
+ }
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ eattr->e_flag |= VFLAG_VERT_SELECTED;
+ }
+}
+
+static void extract_edit_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* WARNING: Adjust #EditLoopData struct accordingly. */
+ GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
+ GPU_vertformat_alias_add(&format, "flag");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+ EditLoopData *vbo_data = GPU_vertbuf_get_data(vbo);
+ *(EditLoopData **)tls_data = vbo_data;
+}
+
+static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ EditLoopData *data = vbo_data + l_index;
+ memset(data, 0x0, sizeof(*data));
+ mesh_render_data_face_flag(mr, f, -1, data);
+ mesh_render_data_edge_flag(mr, l_iter->e, data);
+ mesh_render_data_vert_flag(mr, l_iter->v, data);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ EditLoopData *data = vbo_data + ml_index;
+ memset(data, 0x0, sizeof(*data));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ BMEdge *eed = bm_original_edge_get(mr, ml->e);
+ BMVert *eve = bm_original_vert_get(mr, ml->v);
+ if (efa) {
+ mesh_render_data_face_flag(mr, efa, -1, data);
+ }
+ if (eed) {
+ mesh_render_data_edge_flag(mr, eed, data);
+ }
+ if (eve) {
+ mesh_render_data_vert_flag(mr, eve, data);
+ }
+ }
+}
+
+static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+ EditLoopData *data = vbo_data + mr->loop_len + (ledge_index * 2);
+ memset(data, 0x0, sizeof(*data) * 2);
+ mesh_render_data_edge_flag(mr, eed, &data[0]);
+ data[1] = data[0];
+ mesh_render_data_vert_flag(mr, eed->v1, &data[0]);
+ mesh_render_data_vert_flag(mr, eed->v2, &data[1]);
+}
+
+static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+ EditLoopData *data = vbo_data + mr->loop_len + ledge_index * 2;
+ memset(data, 0x0, sizeof(*data) * 2);
+ const int e_index = mr->ledges[ledge_index];
+ BMEdge *eed = bm_original_edge_get(mr, e_index);
+ BMVert *eve1 = bm_original_vert_get(mr, med->v1);
+ BMVert *eve2 = bm_original_vert_get(mr, med->v2);
+ if (eed) {
+ mesh_render_data_edge_flag(mr, eed, &data[0]);
+ data[1] = data[0];
+ }
+ if (eve1) {
+ mesh_render_data_vert_flag(mr, eve1, &data[0]);
+ }
+ if (eve2) {
+ mesh_render_data_vert_flag(mr, eve2, &data[1]);
+ }
+}
+
+static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EditLoopData *data = vbo_data + offset + lvert_index;
+ memset(data, 0x0, sizeof(*data));
+ mesh_render_data_vert_flag(mr, eve, data);
+}
+
+static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ EditLoopData *data = vbo_data + offset + lvert_index;
+ memset(data, 0x0, sizeof(*data));
+ const int v_index = mr->lverts[lvert_index];
+ BMVert *eve = bm_original_vert_get(mr, v_index);
+ if (eve) {
+ mesh_render_data_vert_flag(mr, eve, data);
+ }
+}
+
+const MeshExtract extract_edit_data = {
+ .init = extract_edit_data_init,
+ .iter_poly_bm = extract_edit_data_iter_poly_bm,
+ .iter_poly_mesh = extract_edit_data_iter_poly_mesh,
+ .iter_ledge_bm = extract_edit_data_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh,
+ .iter_lvert_bm = extract_edit_data_iter_lvert_bm,
+ .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh,
+ .data_type = 0,
+ .data_size = sizeof(EditLoopData *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Data / Flags
+ * \{ */
+
+typedef struct MeshExtract_EditUVData_Data {
+ EditLoopData *vbo_data;
+ int cd_ofs;
+} MeshExtract_EditUVData_Data;
+
+static void extract_edituv_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* WARNING: Adjust #EditLoopData struct accordingly. */
+ GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
+ GPU_vertformat_alias_add(&format, "flag");
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+
+ MeshExtract_EditUVData_Data *data = tls_data;
+ data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
+ data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
+}
+
+static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ MeshExtract_EditUVData_Data *data = _data;
+ EditLoopData *eldata = &data->vbo_data[l_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata);
+ mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata);
+ mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_EditUVData_Data *data = _data;
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ EditLoopData *eldata = &data->vbo_data[ml_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ if (efa) {
+ BMEdge *eed = bm_original_edge_get(mr, ml->e);
+ BMVert *eve = bm_original_vert_get(mr, ml->v);
+ if (eed && eve) {
+ /* Loop on an edge endpoint. */
+ BMLoop *l = BM_face_edge_share_loop(efa, eed);
+ mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata);
+ mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
+ }
+ else {
+ if (eed == NULL) {
+ /* Find if the loop's vert is not part of an edit edge.
+ * For this, we check if the previous loop was on an edge. */
+ const int ml_index_last = mp->loopstart + mp->totloop - 1;
+ const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1);
+ const MLoop *ml_prev = &mr->mloop[l_prev];
+ eed = bm_original_edge_get(mr, ml_prev->e);
+ }
+ if (eed) {
+ /* Mapped points on an edge between two edit verts. */
+ BMLoop *l = BM_face_edge_share_loop(efa, eed);
+ mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
+ }
+ }
+ }
+ }
+}
+
+const MeshExtract extract_edituv_data = {
+ .init = extract_edituv_data_init,
+ .iter_poly_bm = extract_edituv_data_iter_poly_bm,
+ .iter_poly_mesh = extract_edituv_data_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_EditUVData_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV area stretch
+ * \{ */
+
+static void extract_edituv_stretch_area_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+}
+
+BLI_INLINE float area_ratio_get(float area, float uvarea)
+{
+ if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) {
+ /* Tag inversion by using the sign. */
+ return (area > uvarea) ? (uvarea / area) : -(area / uvarea);
+ }
+ return 0.0f;
+}
+
+BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio)
+{
+ ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio;
+ return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
+}
+
+static void extract_edituv_stretch_area_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(data))
+{
+ GPUVertBuf *vbo = buf;
+ float tot_area = 0.0f, tot_uv_area = 0.0f;
+ float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ CustomData *cd_ldata = &mr->bm->ldata;
+ int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
+
+ BMFace *efa;
+ BMIter f_iter;
+ int f;
+ BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
+ float area = BM_face_calc_area(efa);
+ float uvarea = BM_face_calc_area_uv(efa, uv_ofs);
+ tot_area += area;
+ tot_uv_area += uvarea;
+ area_ratio[f] = area_ratio_get(area, uvarea);
+ }
+ }
+ else {
+ BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert);
+ float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data);
+ tot_area += area;
+ tot_uv_area += uvarea;
+ area_ratio[mp_index] = area_ratio_get(area, uvarea);
+ }
+ }
+
+ cache->tot_area = tot_area;
+ cache->tot_uv_area = tot_uv_area;
+
+ /* Convert in place to avoid an extra allocation */
+ uint16_t *poly_stretch = (uint16_t *)area_ratio;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
+ poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX;
+ }
+
+ /* Copy face data for each loop. */
+ uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMFace *efa;
+ BMIter f_iter;
+ int f, l_index = 0;
+ BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
+ for (int i = 0; i < efa->len; i++, l_index++) {
+ loop_stretch[l_index] = poly_stretch[f];
+ }
+ }
+ }
+ else {
+ BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ loop_stretch[l_index] = poly_stretch[mp_index];
+ }
+ }
+ }
+
+ MEM_freeN(area_ratio);
+}
+
+const MeshExtract extract_edituv_stretch_area = {
+ .init = extract_edituv_stretch_area_init,
+ .finish = extract_edituv_stretch_area_finish,
+ .data_type = 0,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV angle stretch
+ * \{ */
+
+typedef struct UVStretchAngle {
+ int16_t angle;
+ int16_t uv_angles[2];
+} UVStretchAngle;
+
+typedef struct MeshExtract_StretchAngle_Data {
+ UVStretchAngle *vbo_data;
+ MLoopUV *luv;
+ float auv[2][2], last_auv[2];
+ float av[2][3], last_av[3];
+ int cd_ofs;
+} MeshExtract_StretchAngle_Data;
+
+static void compute_normalize_edge_vectors(float auv[2][2],
+ float av[2][3],
+ const float uv[2],
+ const float uv_prev[2],
+ const float co[3],
+ const float co_prev[3])
+{
+ /* Move previous edge. */
+ copy_v2_v2(auv[0], auv[1]);
+ copy_v3_v3(av[0], av[1]);
+ /* 2d edge */
+ sub_v2_v2v2(auv[1], uv_prev, uv);
+ normalize_v2(auv[1]);
+ /* 3d edge */
+ sub_v3_v3v3(av[1], co_prev, co);
+ normalize_v3(av[1]);
+}
+
+static short v2_to_short_angle(const float v[2])
+{
+ return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX;
+}
+
+static void edituv_get_edituv_stretch_angle(float auv[2][2],
+ const float av[2][3],
+ UVStretchAngle *r_stretch)
+{
+ /* Send UV's to the shader and let it compute the aspect corrected angle. */
+ r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]);
+ r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]);
+ /* Compute 3D angle here. */
+ r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX;
+
+#if 0 /* here for reference, this is done in shader now. */
+ float uvang = angle_normalized_v2v2(auv0, auv1);
+ float ang = angle_normalized_v3v3(av0, av1);
+ float stretch = fabsf(uvang - ang) / (float)M_PI;
+ return 1.0f - pow2f(1.0f - stretch);
+#endif
+}
+
+static void extract_edituv_stretch_angle_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* Waning: adjust #UVStretchAngle struct accordingly. */
+ GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ MeshExtract_StretchAngle_Data *data = tls_data;
+ data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo);
+
+ /* Special iterator needed to save about half of the computing cost. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
+ }
+ else {
+ BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
+ }
+}
+
+static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_StretchAngle_Data *data = _data;
+ float(*auv)[2] = data->auv, *last_auv = data->last_auv;
+ float(*av)[3] = data->av, *last_av = data->last_av;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ const MLoopUV *luv, *luv_next;
+ BMLoop *l_next = l_iter->next;
+ if (l_iter == BM_FACE_FIRST_LOOP(f)) {
+ /* First loop in face. */
+ BMLoop *l_tmp = l_iter->prev;
+ BMLoop *l_next_tmp = l_iter;
+ luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs);
+ compute_normalize_edge_vectors(auv,
+ av,
+ luv->uv,
+ luv_next->uv,
+ bm_vert_co_get(mr, l_tmp->v),
+ bm_vert_co_get(mr, l_next_tmp->v));
+ /* Save last edge. */
+ copy_v2_v2(last_auv, auv[1]);
+ copy_v3_v3(last_av, av[1]);
+ }
+ if (l_next == BM_FACE_FIRST_LOOP(f)) {
+ /* Move previous edge. */
+ copy_v2_v2(auv[0], auv[1]);
+ copy_v3_v3(av[0], av[1]);
+ /* Copy already calculated last edge. */
+ copy_v2_v2(auv[1], last_auv);
+ copy_v3_v3(av[1], last_av);
+ }
+ else {
+ luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs);
+ compute_normalize_edge_vectors(auv,
+ av,
+ luv->uv,
+ luv_next->uv,
+ bm_vert_co_get(mr, l_iter->v),
+ bm_vert_co_get(mr, l_next->v));
+ }
+ edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_StretchAngle_Data *data = _data;
+
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ float(*auv)[2] = data->auv, *last_auv = data->last_auv;
+ float(*av)[3] = data->av, *last_av = data->last_av;
+ int l_next = ml_index + 1;
+ const MVert *v, *v_next;
+ if (ml_index == mp->loopstart) {
+ /* First loop in face. */
+ const int ml_index_last = ml_index_end - 1;
+ const int l_next_tmp = mp->loopstart;
+ v = &mr->mvert[mr->mloop[ml_index_last].v];
+ v_next = &mr->mvert[mr->mloop[l_next_tmp].v];
+ compute_normalize_edge_vectors(
+ auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co);
+ /* Save last edge. */
+ copy_v2_v2(last_auv, auv[1]);
+ copy_v3_v3(last_av, av[1]);
+ }
+ if (l_next == ml_index_end) {
+ l_next = mp->loopstart;
+ /* Move previous edge. */
+ copy_v2_v2(auv[0], auv[1]);
+ copy_v3_v3(av[0], av[1]);
+ /* Copy already calculated last edge. */
+ copy_v2_v2(auv[1], last_auv);
+ copy_v3_v3(av[1], last_av);
+ }
+ else {
+ v = &mr->mvert[mr->mloop[ml_index].v];
+ v_next = &mr->mvert[mr->mloop[l_next].v];
+ compute_normalize_edge_vectors(
+ auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co);
+ }
+ edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]);
+ }
+}
+
+const MeshExtract extract_edituv_stretch_angle = {
+ .init = extract_edituv_stretch_angle_init,
+ .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm,
+ .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_StretchAngle_Data),
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit Mesh Analysis Colors
+ * \{ */
+
+static void extract_mesh_analysis_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+}
+
+static void axis_from_enum_v3(float v[3], const char axis)
+{
+ zero_v3(v);
+ if (axis < 3) {
+ v[axis] = 1.0f;
+ }
+ else {
+ v[axis - 3] = -1.0f;
+ }
+}
+
+BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange)
+{
+ if (fac < min) {
+ fac = 1.0f;
+ }
+ else if (fac > max) {
+ fac = -1.0f;
+ }
+ else {
+ fac = (fac - min) * minmax_irange;
+ fac = 1.0f - fac;
+ CLAMP(fac, 0.0f, 1.0f);
+ }
+ return fac;
+}
+
+static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang)
+{
+ const MeshStatVis *statvis = &mr->toolsettings->statvis;
+ const float min = statvis->overhang_min / (float)M_PI;
+ const float max = statvis->overhang_max / (float)M_PI;
+ const char axis = statvis->overhang_axis;
+ BMEditMesh *em = mr->edit_bmesh;
+ BMIter iter;
+ BMesh *bm = em->bm;
+ BMFace *f;
+ float dir[3];
+ const float minmax_irange = 1.0f / (max - min);
+
+ BLI_assert(min <= max);
+
+ axis_from_enum_v3(dir, axis);
+
+ /* now convert into global space */
+ mul_transposed_mat3_m4_v3(mr->obmat, dir);
+ normalize_v3(dir);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int l_index = 0;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI;
+ fac = overhang_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_overhang[l_index] = fac;
+ }
+ }
+ }
+ else {
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI;
+ fac = overhang_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_overhang[l_index] = fac;
+ }
+ }
+ }
+}
+
+/**
+ * Needed so we can use jitter values for face interpolation.
+ */
+static void uv_from_jitter_v2(float uv[2])
+{
+ uv[0] += 0.5f;
+ uv[1] += 0.5f;
+ if (uv[0] + uv[1] > 1.0f) {
+ uv[0] = 1.0f - uv[0];
+ uv[1] = 1.0f - uv[1];
+ }
+
+ clamp_v2(uv, 0.0f, 1.0f);
+}
+
+BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange)
+{
+ /* important not '<=' */
+ if (fac < max) {
+ fac = (fac - min) * minmax_irange;
+ fac = 1.0f - fac;
+ CLAMP(fac, 0.0f, 1.0f);
+ }
+ else {
+ fac = -1.0f;
+ }
+ return fac;
+}
+
+static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness)
+{
+ const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */
+ /* cheating to avoid another allocation */
+ float *face_dists = r_thickness + (mr->loop_len - mr->poly_len);
+ BMEditMesh *em = mr->edit_bmesh;
+ const float scale = 1.0f / mat4_to_scale(mr->obmat);
+ const MeshStatVis *statvis = &mr->toolsettings->statvis;
+ const float min = statvis->thickness_min * scale;
+ const float max = statvis->thickness_max * scale;
+ const float minmax_irange = 1.0f / (max - min);
+ const int samples = statvis->thickness_samples;
+ float jit_ofs[32][2];
+ BLI_assert(samples <= 32);
+ BLI_assert(min <= max);
+
+ copy_vn_fl(face_dists, mr->poly_len, max);
+
+ BLI_jitter_init(jit_ofs, samples);
+ for (int j = 0; j < samples; j++) {
+ uv_from_jitter_v2(jit_ofs[j]);
+ }
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMesh *bm = em->bm;
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+
+ struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
+ struct BMLoop *(*looptris)[3] = em->looptris;
+ for (int i = 0; i < mr->tri_len; i++) {
+ BMLoop **ltri = looptris[i];
+ const int index = BM_elem_index_get(ltri[0]->f);
+ const float *cos[3] = {
+ bm_vert_co_get(mr, ltri[0]->v),
+ bm_vert_co_get(mr, ltri[1]->v),
+ bm_vert_co_get(mr, ltri[2]->v),
+ };
+ float ray_co[3];
+ float ray_no[3];
+
+ normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
+
+ for (int j = 0; j < samples; j++) {
+ float dist = face_dists[index];
+ interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
+ madd_v3_v3fl(ray_co, ray_no, eps_offset);
+
+ BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL);
+ if (f_hit && dist < face_dists[index]) {
+ float angle_fac = fabsf(
+ dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit)));
+ angle_fac = 1.0f - angle_fac;
+ angle_fac = angle_fac * angle_fac * angle_fac;
+ angle_fac = 1.0f - angle_fac;
+ dist /= angle_fac;
+ if (dist < face_dists[index]) {
+ face_dists[index] = dist;
+ }
+ }
+ }
+ }
+ BKE_bmbvh_free(bmtree);
+
+ BMIter iter;
+ BMFace *f;
+ int l_index = 0;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ float fac = face_dists[BM_elem_index_get(f)];
+ fac = thickness_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_thickness[l_index] = fac;
+ }
+ }
+ }
+ else {
+ BVHTreeFromMesh treeData = {NULL};
+
+ BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
+ const MLoopTri *mlooptri = mr->mlooptri;
+ for (int i = 0; i < mr->tri_len; i++, mlooptri++) {
+ const int index = mlooptri->poly;
+ const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co,
+ mr->mvert[mr->mloop[mlooptri->tri[1]].v].co,
+ mr->mvert[mr->mloop[mlooptri->tri[2]].v].co};
+ float ray_co[3];
+ float ray_no[3];
+
+ normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
+
+ for (int j = 0; j < samples; j++) {
+ interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
+ madd_v3_v3fl(ray_co, ray_no, eps_offset);
+
+ BVHTreeRayHit hit;
+ hit.index = -1;
+ hit.dist = face_dists[index];
+ if ((BLI_bvhtree_ray_cast(
+ tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) &&
+ hit.dist < face_dists[index]) {
+ float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no));
+ angle_fac = 1.0f - angle_fac;
+ angle_fac = angle_fac * angle_fac * angle_fac;
+ angle_fac = 1.0f - angle_fac;
+ hit.dist /= angle_fac;
+ if (hit.dist < face_dists[index]) {
+ face_dists[index] = hit.dist;
+ }
+ }
+ }
+ }
+
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float fac = face_dists[mp_index];
+ fac = thickness_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_thickness[l_index] = fac;
+ }
+ }
+ }
+}
+
+struct BVHTree_OverlapData {
+ const Mesh *me;
+ const MLoopTri *mlooptri;
+ float epsilon;
+};
+
+static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
+{
+ struct BVHTree_OverlapData *data = userdata;
+ const Mesh *me = data->me;
+
+ const MLoopTri *tri_a = &data->mlooptri[index_a];
+ const MLoopTri *tri_b = &data->mlooptri[index_b];
+
+ if (UNLIKELY(tri_a->poly == tri_b->poly)) {
+ return false;
+ }
+
+ const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co,
+ me->mvert[me->mloop[tri_a->tri[1]].v].co,
+ me->mvert[me->mloop[tri_a->tri[2]].v].co};
+ const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co,
+ me->mvert[me->mloop[tri_b->tri[1]].v].co,
+ me->mvert[me->mloop[tri_b->tri[2]].v].co};
+ float ix_pair[2][3];
+ int verts_shared = 0;
+
+ verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) +
+ ELEM(tri_a_co[2], UNPACK3(tri_b_co)));
+
+ /* if 2 points are shared, bail out */
+ if (verts_shared >= 2) {
+ return false;
+ }
+
+ return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) &&
+ /* if we share a vertex, check the intersection isn't a 'point' */
+ ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon)));
+}
+
+static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
+{
+ BMEditMesh *em = mr->edit_bmesh;
+
+ for (int l_index = 0; l_index < mr->loop_len; l_index++) {
+ r_intersect[l_index] = -1.0f;
+ }
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ uint overlap_len;
+ BMesh *bm = em->bm;
+
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+
+ struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
+ BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len);
+
+ if (overlap) {
+ for (int i = 0; i < overlap_len; i++) {
+ BMFace *f_hit_pair[2] = {
+ em->looptris[overlap[i].indexA][0]->f,
+ em->looptris[overlap[i].indexB][0]->f,
+ };
+ for (int j = 0; j < 2; j++) {
+ BMFace *f_hit = f_hit_pair[j];
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit);
+ int l_index = BM_elem_index_get(l_first);
+ for (int k = 0; k < f_hit->len; k++, l_index++) {
+ r_intersect[l_index] = 1.0f;
+ }
+ }
+ }
+ MEM_freeN(overlap);
+ }
+
+ BKE_bmbvh_free(bmtree);
+ }
+ else {
+ uint overlap_len;
+ BVHTreeFromMesh treeData = {NULL};
+
+ BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
+
+ struct BVHTree_OverlapData data = {
+ .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)};
+
+ BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data);
+ if (overlap) {
+ for (int i = 0; i < overlap_len; i++) {
+ const MPoly *f_hit_pair[2] = {
+ &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly],
+ &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly],
+ };
+ for (int j = 0; j < 2; j++) {
+ const MPoly *f_hit = f_hit_pair[j];
+ int l_index = f_hit->loopstart;
+ for (int k = 0; k < f_hit->totloop; k++, l_index++) {
+ r_intersect[l_index] = 1.0f;
+ }
+ }
+ }
+ MEM_freeN(overlap);
+ }
+ }
+}
+
+BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange)
+{
+ if (fac >= min) {
+ fac = (fac - min) * minmax_irange;
+ CLAMP(fac, 0.0f, 1.0f);
+ }
+ else {
+ /* fallback */
+ fac = -1.0f;
+ }
+ return fac;
+}
+
+static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
+{
+ BMEditMesh *em = mr->edit_bmesh;
+ const MeshStatVis *statvis = &mr->toolsettings->statvis;
+ const float min = statvis->distort_min;
+ const float max = statvis->distort_max;
+ const float minmax_irange = 1.0f / (max - min);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMesh *bm = em->bm;
+ BMFace *f;
+
+ if (mr->bm_vert_coords != NULL) {
+ BKE_editmesh_cache_ensure_poly_normals(em, mr->edit_data);
+
+ /* Most likely this is already valid, ensure just in case.
+ * Needed for #BM_loop_calc_face_normal_safe_vcos. */
+ BM_mesh_elem_index_ensure(em->bm, BM_VERT);
+ }
+
+ int l_index = 0;
+ int f_index = 0;
+ BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) {
+ float fac = -1.0f;
+
+ if (f->len > 3) {
+ BMLoop *l_iter, *l_first;
+
+ fac = 0.0f;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const float *no_face;
+ float no_corner[3];
+ if (mr->bm_vert_coords != NULL) {
+ no_face = mr->bm_poly_normals[f_index];
+ BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner);
+ }
+ else {
+ no_face = f->no;
+ BM_loop_calc_face_normal_safe(l_iter, no_corner);
+ }
+
+ /* simple way to detect (what is most likely) concave */
+ if (dot_v3v3(no_face, no_corner) < 0.0f) {
+ negate_v3(no_corner);
+ }
+ fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner));
+
+ } while ((l_iter = l_iter->next) != l_first);
+ fac *= 2.0f;
+ }
+
+ fac = distort_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_distort[l_index] = fac;
+ }
+ }
+ }
+ else {
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float fac = -1.0f;
+
+ if (mp->totloop > 3) {
+ float *f_no = mr->poly_normals[mp_index];
+ fac = 0.0f;
+
+ for (int i = 1; i <= mp->totloop; i++) {
+ const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop];
+ const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
+ const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
+ float no_corner[3];
+ normal_tri_v3(no_corner,
+ mr->mvert[l_prev->v].co,
+ mr->mvert[l_curr->v].co,
+ mr->mvert[l_next->v].co);
+ /* simple way to detect (what is most likely) concave */
+ if (dot_v3v3(f_no, no_corner) < 0.0f) {
+ negate_v3(no_corner);
+ }
+ fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner));
+ }
+ fac *= 2.0f;
+ }
+
+ fac = distort_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_distort[l_index] = fac;
+ }
+ }
+ }
+}
+
+BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange)
+{
+ /* important not '>=' */
+ if (fac > min) {
+ fac = (fac - min) * minmax_irange;
+ CLAMP(fac, 0.0f, 1.0f);
+ }
+ else {
+ /* fallback */
+ fac = -1.0f;
+ }
+ return fac;
+}
+
+static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
+{
+ BMEditMesh *em = mr->edit_bmesh;
+ const MeshStatVis *statvis = &mr->toolsettings->statvis;
+ const float min = statvis->sharp_min;
+ const float max = statvis->sharp_max;
+ const float minmax_irange = 1.0f / (max - min);
+
+ /* Can we avoid this extra allocation? */
+ float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__);
+ copy_vn_fl(vert_angles, mr->vert_len, -M_PI);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMesh *bm = em->bm;
+ BMFace *efa;
+ BMEdge *e;
+ /* first assign float values to verts */
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ float angle = BM_edge_calc_face_angle_signed(e);
+ float *col1 = &vert_angles[BM_elem_index_get(e->v1)];
+ float *col2 = &vert_angles[BM_elem_index_get(e->v2)];
+ *col1 = max_ff(*col1, angle);
+ *col2 = max_ff(*col2, angle);
+ }
+ /* Copy vert value to loops. */
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ int l_index = BM_elem_index_get(l_iter);
+ int v_index = BM_elem_index_get(l_iter->v);
+ r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ /* first assign float values to verts */
+ const MPoly *mp = mr->mpoly;
+
+ EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len);
+
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ for (int i = 0; i < mp->totloop; i++) {
+ const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
+ const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
+ const MVert *v_curr = &mr->mvert[l_curr->v];
+ const MVert *v_next = &mr->mvert[l_next->v];
+ float angle;
+ void **pval;
+ bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval);
+ if (!value_is_init) {
+ *pval = mr->poly_normals[mp_index];
+ /* non-manifold edge, yet... */
+ continue;
+ }
+ if (*pval != NULL) {
+ const float *f1_no = mr->poly_normals[mp_index];
+ const float *f2_no = *pval;
+ angle = angle_normalized_v3v3(f1_no, f2_no);
+ angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle;
+ /* Tag as manifold. */
+ *pval = NULL;
+ }
+ else {
+ /* non-manifold edge */
+ angle = DEG2RADF(90.0f);
+ }
+ float *col1 = &vert_angles[l_curr->v];
+ float *col2 = &vert_angles[l_next->v];
+ *col1 = max_ff(*col1, angle);
+ *col2 = max_ff(*col2, angle);
+ }
+ }
+ /* Remaining non manifold edges. */
+ EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh);
+ for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+ if (BLI_edgehashIterator_getValue(ehi) != NULL) {
+ uint v1, v2;
+ const float angle = DEG2RADF(90.0f);
+ BLI_edgehashIterator_getKey(ehi, &v1, &v2);
+ float *col1 = &vert_angles[v1];
+ float *col2 = &vert_angles[v2];
+ *col1 = max_ff(*col1, angle);
+ *col2 = max_ff(*col2, angle);
+ }
+ }
+ BLI_edgehashIterator_free(ehi);
+ BLI_edgehash_free(eh, NULL);
+
+ const MLoop *ml = mr->mloop;
+ for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) {
+ r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange);
+ }
+ }
+
+ MEM_freeN(vert_angles);
+}
+
+static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(data))
+{
+ GPUVertBuf *vbo = buf;
+ BLI_assert(mr->edit_bmesh);
+
+ float *l_weight = (float *)GPU_vertbuf_get_data(vbo);
+
+ switch (mr->toolsettings->statvis.type) {
+ case SCE_STATVIS_OVERHANG:
+ statvis_calc_overhang(mr, l_weight);
+ break;
+ case SCE_STATVIS_THICKNESS:
+ statvis_calc_thickness(mr, l_weight);
+ break;
+ case SCE_STATVIS_INTERSECT:
+ statvis_calc_intersect(mr, l_weight);
+ break;
+ case SCE_STATVIS_DISTORT:
+ statvis_calc_distort(mr, l_weight);
+ break;
+ case SCE_STATVIS_SHARP:
+ statvis_calc_sharp(mr, l_weight);
+ break;
+ }
+}
+
+const MeshExtract extract_mesh_analysis = {
+ .init = extract_mesh_analysis_init,
+ .finish = extract_analysis_iter_finish_mesh,
+ /* This is not needed for all visualization types.
+ * * Maybe split into different extract. */
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots positions
+ * \{ */
+
+static void extract_fdots_pos_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+ *(float(**)[3])tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int f_index,
+ void *data)
+{
+ float(*center)[3] = *(float(**)[3])data;
+
+ float *co = center[f_index];
+ zero_v3(co);
+
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ add_v3_v3(co, bm_vert_co_get(mr, l_iter->v));
+ } while ((l_iter = l_iter->next) != l_first);
+ mul_v3_fl(co, 1.0f / (float)f->len);
+}
+
+static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data)
+{
+ float(*center)[3] = *(float(**)[3])data;
+ float *co = center[mp_index];
+ zero_v3(co);
+
+ const MVert *mvert = mr->mvert;
+ const MLoop *mloop = mr->mloop;
+
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ if (mr->use_subsurf_fdots) {
+ const MVert *mv = &mr->mvert[ml->v];
+ if (mv->flag & ME_VERT_FACEDOT) {
+ copy_v3_v3(center[mp_index], mv->co);
+ break;
+ }
+ }
+ else {
+ const MVert *mv = &mvert[ml->v];
+ add_v3_v3(center[mp_index], mv->co);
+ }
+ }
+
+ if (!mr->use_subsurf_fdots) {
+ mul_v3_fl(co, 1.0f / (float)mp->totloop);
+ }
+}
+
+const MeshExtract extract_fdots_pos = {
+ .init = extract_fdots_pos_init,
+ .iter_poly_bm = extract_fdots_pos_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(float (*)[3]),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots Normal and edit flag
+ * \{ */
+#define NOR_AND_FLAG_DEFAULT 0
+#define NOR_AND_FLAG_SELECT 1
+#define NOR_AND_FLAG_ACTIVE -1
+#define NOR_AND_FLAG_HIDDEN -2
+
+static void extract_fdots_nor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+}
+
+static void extract_fdots_nor_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(data))
+{
+ GPUVertBuf *vbo = buf;
+ static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
+ GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
+ BMFace *efa;
+
+ /* Quicker than doing it for each loop. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ for (int f = 0; f < mr->poly_len; f++) {
+ efa = BM_face_at_index(mr->bm, f);
+ const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
+ if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[f] == ORIGINDEX_NONE)) {
+ nor[f] = GPU_normal_convert_i10_v3(invalid_normal);
+ nor[f].w = NOR_AND_FLAG_HIDDEN;
+ }
+ else {
+ nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
+ /* Select / Active Flag. */
+ nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
+ ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
+ NOR_AND_FLAG_DEFAULT);
+ }
+ }
+ }
+ else {
+ for (int f = 0; f < mr->poly_len; f++) {
+ efa = bm_original_face_get(mr, f);
+ const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
+ if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[f] == ORIGINDEX_NONE)) {
+ nor[f] = GPU_normal_convert_i10_v3(invalid_normal);
+ nor[f].w = NOR_AND_FLAG_HIDDEN;
+ }
+ else {
+ nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
+ /* Select / Active Flag. */
+ nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
+ ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
+ NOR_AND_FLAG_DEFAULT);
+ }
+ }
+ }
+}
+
+const MeshExtract extract_fdots_nor = {
+ .init = extract_fdots_nor_init,
+ .finish = extract_fdots_nor_finish,
+ .data_type = MR_DATA_POLY_NOR,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots High Quality Normal and edit flag
+ * \{ */
+static void extract_fdots_nor_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+}
+
+static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(data))
+{
+ GPUVertBuf *vbo = buf;
+ static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
+ short *nor = (short *)GPU_vertbuf_get_data(vbo);
+ BMFace *efa;
+
+ /* Quicker than doing it for each loop. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ for (int f = 0; f < mr->poly_len; f++) {
+ efa = BM_face_at_index(mr->bm, f);
+ const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
+ if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[f] == ORIGINDEX_NONE)) {
+ normal_float_to_short_v3(&nor[f * 4], invalid_normal);
+ nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
+ }
+ else {
+ normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
+ /* Select / Active Flag. */
+ nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
+ ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
+ NOR_AND_FLAG_DEFAULT);
+ }
+ }
+ }
+ else {
+ for (int f = 0; f < mr->poly_len; f++) {
+ efa = bm_original_face_get(mr, f);
+ const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
+ if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[f] == ORIGINDEX_NONE)) {
+ normal_float_to_short_v3(&nor[f * 4], invalid_normal);
+ nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
+ }
+ else {
+ normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
+ /* Select / Active Flag. */
+ nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
+ ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
+ NOR_AND_FLAG_DEFAULT);
+ }
+ }
+ }
+}
+
+const MeshExtract extract_fdots_nor_hq = {
+ .init = extract_fdots_nor_hq_init,
+ .finish = extract_fdots_nor_hq_finish,
+ .data_type = MR_DATA_POLY_NOR,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots UV
+ * \{ */
+
+typedef struct MeshExtract_FdotUV_Data {
+ float (*vbo_data)[2];
+ MLoopUV *uv_data;
+ int cd_ofs;
+} MeshExtract_FdotUV_Data;
+
+static void extract_fdots_uv_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ GPU_vertformat_alias_add(&format, "au");
+ GPU_vertformat_alias_add(&format, "pos");
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+
+ if (!mr->use_subsurf_fdots) {
+ /* Clear so we can accumulate on it. */
+ memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride);
+ }
+
+ MeshExtract_FdotUV_Data *data = tls_data;
+ data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
+ }
+ else {
+ data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
+ }
+}
+
+static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_FdotUV_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ float w = 1.0f / (float)f->len;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs);
+ madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_FdotUV_Data *data = _data;
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ if (mr->use_subsurf_fdots) {
+ const MVert *mv = &mr->mvert[ml->v];
+ if (mv->flag & ME_VERT_FACEDOT) {
+ copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv);
+ }
+ }
+ else {
+ float w = 1.0f / (float)mp->totloop;
+ madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w);
+ }
+ }
+}
+
+const MeshExtract extract_fdots_uv = {
+ .init = extract_fdots_uv_init,
+ .iter_poly_bm = extract_fdots_uv_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_FdotUV_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots Edit UV flag
+ * \{ */
+
+typedef struct MeshExtract_EditUVFdotData_Data {
+ EditLoopData *vbo_data;
+ int cd_ofs;
+} MeshExtract_EditUVFdotData_Data;
+
+static void extract_fdots_edituv_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+
+ MeshExtract_EditUVFdotData_Data *data = tls_data;
+ data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
+ data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
+}
+
+static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_EditUVFdotData_Data *data = _data;
+ EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)];
+ memset(eldata, 0x0, sizeof(*eldata));
+ mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata);
+}
+
+static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *UNUSED(mp),
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_EditUVFdotData_Data *data = _data;
+ EditLoopData *eldata = &data->vbo_data[mp_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ if (efa) {
+ mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata);
+ }
+}
+
+const MeshExtract extract_fdots_edituv_data = {
+ .init = extract_fdots_edituv_data_init,
+ .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_EditUVFdotData_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Skin Modifier Roots
+ * \{ */
+
+typedef struct SkinRootData {
+ float size;
+ float local_pos[3];
+} SkinRootData;
+
+static void extract_skin_roots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ /* Exclusively for edit mode. */
+ BLI_assert(mr->bm);
+
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->bm->totvert);
+
+ SkinRootData *vbo_data = (SkinRootData *)GPU_vertbuf_get_data(vbo);
+
+ int root_len = 0;
+ int cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MVERT_SKIN);
+
+ BMIter iter;
+ BMVert *eve;
+ BM_ITER_MESH (eve, &iter, mr->bm, BM_VERTS_OF_MESH) {
+ const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_ofs);
+ if (vs->flag & MVERT_SKIN_ROOT) {
+ vbo_data->size = (vs->radius[0] + vs->radius[1]) * 0.5f;
+ copy_v3_v3(vbo_data->local_pos, bm_vert_co_get(mr, eve));
+ vbo_data++;
+ root_len++;
+ }
+ }
+
+ /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */
+ GPU_vertbuf_data_len_set(vbo, root_len);
+}
+
+const MeshExtract extract_skin_roots = {
+ .init = extract_skin_roots_init,
+ .data_type = 0,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Selection Index
+ * \{ */
+
+static void extract_select_idx_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* TODO rename "color" to something more descriptive. */
+ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+ *(uint32_t **)tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the
+ * select element associated with this loop ID. This would remove the need for this separate
+ * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the
+ * shader to output original index. */
+
+static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int f_index,
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ (*(uint32_t **)data)[l_index] = f_index;
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ (*(uint32_t **)data)[l_index] = BM_elem_index_get(l_iter->e);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ (*(uint32_t **)data)[l_index] = BM_elem_index_get(l_iter->v);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *data)
+{
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed);
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed);
+}
+
+static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *data)
+{
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1);
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2);
+}
+
+static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *data)
+{
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ (*(uint32_t **)data)[offset + lvert_index] = BM_elem_index_get(eve);
+}
+
+static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data)
+{
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ (*(uint32_t **)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index;
+ }
+}
+
+static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ (*(uint32_t **)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e;
+ }
+}
+
+static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ (*(uint32_t **)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v;
+ }
+}
+
+static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *UNUSED(med),
+ const int ledge_index,
+ void *data)
+{
+ const int e_index = mr->ledges[ledge_index];
+ const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index;
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig;
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig;
+}
+
+static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *data)
+{
+ int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1;
+ int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2;
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig;
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig;
+}
+
+static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
+ void *data)
+{
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int v_index = mr->lverts[lvert_index];
+ const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index;
+ (*(uint32_t **)data)[offset + lvert_index] = v_orig;
+}
+
+const MeshExtract extract_poly_idx = {
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_poly_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_poly_idx_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(uint32_t *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)};
+
+const MeshExtract extract_edge_idx = {
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_edge_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_edge_idx_iter_poly_mesh,
+ .iter_ledge_bm = extract_edge_idx_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh,
+ .data_type = 0,
+ .data_size = sizeof(uint32_t *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)};
+
+const MeshExtract extract_vert_idx = {
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_vert_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_vert_idx_iter_poly_mesh,
+ .iter_ledge_bm = extract_vert_idx_iter_ledge_bm,
+ .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh,
+ .iter_lvert_bm = extract_vert_idx_iter_lvert_bm,
+ .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh,
+ .data_type = 0,
+ .data_size = sizeof(uint32_t *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)};
+
+static void extract_fdot_idx_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* TODO rename "color" to something more descriptive. */
+ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+ *(uint32_t **)tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *UNUSED(f),
+ const int f_index,
+ void *data)
+{
+ (*(uint32_t **)data)[f_index] = f_index;
+}
+
+static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *UNUSED(mp),
+ const int mp_index,
+ void *data)
+{
+ if (mr->p_origindex != NULL) {
+ (*(uint32_t **)data)[mp_index] = mr->p_origindex[mp_index];
+ }
+ else {
+ (*(uint32_t **)data)[mp_index] = mp_index;
+ }
+}
+
+const MeshExtract extract_fdot_idx = {
+ .init = extract_fdot_idx_init,
+ .iter_poly_bm = extract_fdot_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(uint32_t *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)};
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h
new file mode 100644
index 00000000000..2ece0b4f1db
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h
@@ -0,0 +1,295 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+
+#pragma once
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_editmesh.h"
+
+#include "draw_cache_extract.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum eMRExtractType {
+ MR_EXTRACT_BMESH,
+ MR_EXTRACT_MAPPED,
+ MR_EXTRACT_MESH,
+} eMRExtractType;
+
+typedef struct MeshRenderData {
+ eMRExtractType extract_type;
+
+ int poly_len, edge_len, vert_len, loop_len;
+ int edge_loose_len;
+ int vert_loose_len;
+ int loop_loose_len;
+ int tri_len;
+ int mat_len;
+
+ bool use_hide;
+ bool use_subsurf_fdots;
+ bool use_final_mesh;
+
+ /** Use for #MeshStatVis calculation which use world-space coords. */
+ float obmat[4][4];
+
+ const ToolSettings *toolsettings;
+ /** Edit Mesh */
+ BMEditMesh *edit_bmesh;
+ BMesh *bm;
+ EditMeshData *edit_data;
+
+ /* For deformed edit-mesh data. */
+ /* Use for #ME_WRAPPER_TYPE_BMESH. */
+ const float (*bm_vert_coords)[3];
+ const float (*bm_vert_normals)[3];
+ const float (*bm_poly_normals)[3];
+ const float (*bm_poly_centers)[3];
+
+ int *v_origindex, *e_origindex, *p_origindex;
+ int crease_ofs;
+ int bweight_ofs;
+ int freestyle_edge_ofs;
+ int freestyle_face_ofs;
+ /** Mesh */
+ Mesh *me;
+ const MVert *mvert;
+ const MEdge *medge;
+ const MLoop *mloop;
+ const MPoly *mpoly;
+ BMVert *eve_act;
+ BMEdge *eed_act;
+ BMFace *efa_act;
+ BMFace *efa_act_uv;
+ /* Data created on-demand (usually not for #BMesh based data). */
+ MLoopTri *mlooptri;
+ float (*loop_normals)[3];
+ float (*poly_normals)[3];
+ int *lverts, *ledges;
+} MeshRenderData;
+
+BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
+{
+ return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+ BM_face_at_index(mr->bm, mr->p_origindex[idx]) :
+ NULL;
+}
+
+BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx)
+{
+ return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+ BM_edge_at_index(mr->bm, mr->e_origindex[idx]) :
+ NULL;
+}
+
+BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx)
+{
+ return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+ BM_vert_at_index(mr->bm, mr->v_origindex[idx]) :
+ NULL;
+}
+
+BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve)
+{
+ const float(*vert_coords)[3] = mr->bm_vert_coords;
+ if (vert_coords != NULL) {
+ return vert_coords[BM_elem_index_get(eve)];
+ }
+
+ UNUSED_VARS(mr);
+ return eve->co;
+}
+
+BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve)
+{
+ const float(*vert_normals)[3] = mr->bm_vert_normals;
+ if (vert_normals != NULL) {
+ return vert_normals[BM_elem_index_get(eve)];
+ }
+
+ UNUSED_VARS(mr);
+ return eve->no;
+}
+
+BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa)
+{
+ const float(*poly_normals)[3] = mr->bm_poly_normals;
+ if (poly_normals != NULL) {
+ return poly_normals[BM_elem_index_get(efa)];
+ }
+
+ UNUSED_VARS(mr);
+ return efa->no;
+}
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract Struct
+ * \{ */
+/* TODO(jbakker): move parameters inside a struct. */
+typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr,
+ BMLoop **elt,
+ const int elt_index,
+ void *data);
+typedef void(ExtractTriMeshFn)(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int elt_index,
+ void *data);
+typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr,
+ const BMFace *f,
+ const int f_index,
+ void *data);
+typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data);
+typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *data);
+typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *data);
+typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *data);
+typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr,
+ const MVert *mv,
+ const int lvert_index,
+ void *data);
+typedef void(ExtractInitFn)(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buffer,
+ void *r_data);
+typedef void(ExtractFinishFn)(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buffer,
+ void *data);
+typedef void(ExtractTaskReduceFn)(void *userdata, void *task_userdata);
+
+typedef struct MeshExtract {
+ /** Executed on main thread and return user data for iteration functions. */
+ ExtractInitFn *init;
+ /** Executed on one (or more if use_threading) worker thread(s). */
+ ExtractTriBMeshFn *iter_looptri_bm;
+ ExtractTriMeshFn *iter_looptri_mesh;
+ ExtractPolyBMeshFn *iter_poly_bm;
+ ExtractPolyMeshFn *iter_poly_mesh;
+ ExtractLEdgeBMeshFn *iter_ledge_bm;
+ ExtractLEdgeMeshFn *iter_ledge_mesh;
+ ExtractLVertBMeshFn *iter_lvert_bm;
+ ExtractLVertMeshFn *iter_lvert_mesh;
+ /** Executed on one worker thread after all elements iterations. */
+ ExtractTaskReduceFn *task_reduce;
+ ExtractFinishFn *finish;
+ /** Used to request common data. */
+ eMRDataType data_type;
+ size_t data_size;
+ /** Used to know if the element callbacks are thread-safe and can be parallelized. */
+ bool use_threading;
+ /**
+ * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index
+ * buffer.
+ */
+ size_t mesh_buffer_offset;
+} MeshExtract;
+
+/** \} */
+
+/* draw_cache_extract_mesh_render_data.c */
+MeshRenderData *mesh_render_data_create(Mesh *me,
+ MeshBufferExtractionCache *cache,
+ const bool is_editmode,
+ const bool is_paint_mode,
+ const bool is_mode_active,
+ const float obmat[4][4],
+ const bool do_final,
+ const bool do_uvedit,
+ const ToolSettings *ts,
+ const eMRIterType iter_type);
+void mesh_render_data_free(MeshRenderData *mr);
+void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag);
+void mesh_render_data_update_looptris(MeshRenderData *mr,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag);
+
+/* draw_cache_extract_mesh_extractors.c */
+void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc);
+eMRIterType mesh_extract_iter_type(const MeshExtract *ext);
+const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
+ const bool do_hq_normals,
+ const bool do_single_mat);
+
+extern const MeshExtract extract_tris;
+extern const MeshExtract extract_tris_single_mat;
+extern const MeshExtract extract_lines;
+extern const MeshExtract extract_lines_with_lines_loose;
+extern const MeshExtract extract_lines_loose_only;
+extern const MeshExtract extract_points;
+extern const MeshExtract extract_fdots;
+extern const MeshExtract extract_lines_paint_mask;
+extern const MeshExtract extract_lines_adjacency;
+extern const MeshExtract extract_edituv_tris;
+extern const MeshExtract extract_edituv_lines;
+extern const MeshExtract extract_edituv_points;
+extern const MeshExtract extract_edituv_fdots;
+extern const MeshExtract extract_pos_nor;
+extern const MeshExtract extract_pos_nor_hq;
+extern const MeshExtract extract_lnor_hq;
+extern const MeshExtract extract_lnor;
+extern const MeshExtract extract_uv;
+extern const MeshExtract extract_tan;
+extern const MeshExtract extract_tan_hq;
+extern const MeshExtract extract_sculpt_data;
+extern const MeshExtract extract_vcol;
+extern const MeshExtract extract_orco;
+extern const MeshExtract extract_edge_fac;
+extern const MeshExtract extract_weights;
+extern const MeshExtract extract_edit_data;
+extern const MeshExtract extract_edituv_data;
+extern const MeshExtract extract_edituv_stretch_area;
+extern const MeshExtract extract_edituv_stretch_angle;
+extern const MeshExtract extract_mesh_analysis;
+extern const MeshExtract extract_fdots_pos;
+extern const MeshExtract extract_fdots_nor;
+extern const MeshExtract extract_fdots_nor_hq;
+extern const MeshExtract extract_fdots_uv;
+extern const MeshExtract extract_fdots_edituv_data;
+extern const MeshExtract extract_skin_roots;
+extern const MeshExtract extract_poly_idx;
+extern const MeshExtract extract_edge_idx;
+extern const MeshExtract extract_vert_idx;
+extern const MeshExtract extract_fdot_idx;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
new file mode 100644
index 00000000000..4741bcb06c9
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
@@ -0,0 +1,371 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_math.h"
+
+#include "BKE_editmesh.h"
+#include "BKE_editmesh_cache.h"
+#include "BKE_mesh.h"
+
+#include "GPU_batch.h"
+
+#include "ED_mesh.h"
+
+#include "draw_cache_extract_mesh_private.h"
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
+ * \{ */
+
+static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache)
+{
+ mr->ledges = cache->ledges;
+ mr->lverts = cache->lverts;
+ mr->vert_loose_len = cache->vert_loose_len;
+ mr->edge_loose_len = cache->edge_loose_len;
+
+ mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2);
+}
+
+static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr,
+ MeshBufferExtractionCache *cache)
+{
+ /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges
+ * and verts are calculated at the same time.*/
+ if (cache->lverts) {
+ return;
+ }
+
+ cache->vert_loose_len = 0;
+ cache->edge_loose_len = 0;
+
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+
+ BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
+
+ cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__);
+ const MEdge *med = mr->medge;
+ for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) {
+ if (med->flag & ME_LOOSEEDGE) {
+ cache->ledges[cache->edge_loose_len++] = med_index;
+ }
+ /* Tag verts as not loose. */
+ BLI_BITMAP_ENABLE(lvert_map, med->v1);
+ BLI_BITMAP_ENABLE(lvert_map, med->v2);
+ }
+ if (cache->edge_loose_len < mr->edge_len) {
+ cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges));
+ }
+
+ cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
+ for (int v = 0; v < mr->vert_len; v++) {
+ if (!BLI_BITMAP_TEST(lvert_map, v)) {
+ cache->lverts[cache->vert_loose_len++] = v;
+ }
+ }
+ if (cache->vert_loose_len < mr->vert_len) {
+ cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts));
+ }
+
+ MEM_freeN(lvert_map);
+ }
+ else {
+ /* #BMesh */
+ BMesh *bm = mr->bm;
+ int elem_id;
+ BMIter iter;
+ BMVert *eve;
+ BMEdge *ede;
+
+ cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__);
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) {
+ if (eve->e == NULL) {
+ cache->lverts[cache->vert_loose_len++] = elem_id;
+ }
+ }
+ if (cache->vert_loose_len < mr->vert_len) {
+ cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts));
+ }
+
+ cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__);
+ BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) {
+ if (ede->l == NULL) {
+ cache->ledges[cache->edge_loose_len++] = elem_id;
+ }
+ }
+ if (cache->edge_loose_len < mr->edge_len) {
+ cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges));
+ }
+ }
+}
+
+/**
+ * Part of the creation of the #MeshRenderData that happens in a thread.
+ */
+void mesh_render_data_update_looptris(MeshRenderData *mr,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag)
+{
+ Mesh *me = mr->me;
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
+ mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI");
+ BKE_mesh_recalc_looptri(
+ me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri);
+ }
+ }
+ else {
+ /* #BMesh */
+ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
+ /* Edit mode ensures this is valid, no need to calculate. */
+ BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL));
+ }
+ }
+}
+
+void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag)
+{
+ Mesh *me = mr->me;
+ const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
+ const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
+
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
+ mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__);
+ BKE_mesh_calc_normals_poly((MVert *)mr->mvert,
+ NULL,
+ mr->vert_len,
+ mr->mloop,
+ mr->mpoly,
+ mr->loop_len,
+ mr->poly_len,
+ mr->poly_normals,
+ true);
+ }
+ if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
+ mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
+ short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL);
+ BKE_mesh_normals_loop_split(mr->me->mvert,
+ mr->vert_len,
+ mr->me->medge,
+ mr->edge_len,
+ mr->me->mloop,
+ mr->loop_normals,
+ mr->loop_len,
+ mr->me->mpoly,
+ mr->poly_normals,
+ mr->poly_len,
+ is_auto_smooth,
+ split_angle,
+ NULL,
+ clnors,
+ NULL);
+ }
+ }
+ else {
+ /* #BMesh */
+ if (data_flag & MR_DATA_POLY_NOR) {
+ /* Use #BMFace.no instead. */
+ }
+ if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
+
+ const float(*vert_coords)[3] = NULL;
+ const float(*vert_normals)[3] = NULL;
+ const float(*poly_normals)[3] = NULL;
+
+ if (mr->edit_data && mr->edit_data->vertexCos) {
+ vert_coords = mr->bm_vert_coords;
+ vert_normals = mr->bm_vert_normals;
+ poly_normals = mr->bm_poly_normals;
+ }
+
+ mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
+ const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
+ BM_loops_calc_normal_vcos(mr->bm,
+ vert_coords,
+ vert_normals,
+ poly_normals,
+ is_auto_smooth,
+ split_angle,
+ mr->loop_normals,
+ NULL,
+ NULL,
+ clnors_offset,
+ false);
+ }
+ }
+}
+
+/**
+ * \param is_mode_active: When true, use the modifiers from the edit-data,
+ * otherwise don't use modifiers as they are not from this object.
+ */
+MeshRenderData *mesh_render_data_create(Mesh *me,
+ MeshBufferExtractionCache *cache,
+ const bool is_editmode,
+ const bool is_paint_mode,
+ const bool is_mode_active,
+ const float obmat[4][4],
+ const bool do_final,
+ const bool do_uvedit,
+ const ToolSettings *ts,
+ const eMRIterType iter_type)
+{
+ MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__);
+ mr->toolsettings = ts;
+ mr->mat_len = mesh_render_mat_len_get(me);
+
+ copy_m4_m4(mr->obmat, obmat);
+
+ if (is_editmode) {
+ BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final);
+ mr->bm = me->edit_mesh->bm;
+ mr->edit_bmesh = me->edit_mesh;
+ mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage;
+ mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL;
+
+ if (mr->edit_data) {
+ EditMeshData *emd = mr->edit_data;
+ if (emd->vertexCos) {
+ BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd);
+ BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd);
+ }
+
+ mr->bm_vert_coords = mr->edit_data->vertexCos;
+ mr->bm_vert_normals = mr->edit_data->vertexNos;
+ mr->bm_poly_normals = mr->edit_data->polyNos;
+ mr->bm_poly_centers = mr->edit_data->polyCos;
+ }
+
+ bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA);
+ bool use_mapped = is_mode_active &&
+ (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original);
+
+ int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE;
+
+ BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types);
+ BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP);
+
+ mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false);
+ mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true);
+ mr->eed_act = BM_mesh_active_edge_get(mr->bm);
+ mr->eve_act = BM_mesh_active_vert_get(mr->bm);
+
+ mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE);
+ mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT);
+#ifdef WITH_FREESTYLE
+ mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE);
+ mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE);
+#endif
+
+ if (use_mapped) {
+ mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
+ mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
+ mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
+
+ use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
+ }
+
+ mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH;
+
+ /* Seems like the mesh_eval_final do not have the right origin indices.
+ * Force not mapped in this case. */
+ if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) {
+ // mr->edit_bmesh = NULL;
+ mr->extract_type = MR_EXTRACT_MESH;
+ }
+ }
+ else {
+ mr->me = me;
+ mr->edit_bmesh = NULL;
+
+ bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original;
+ if (use_mapped) {
+ mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
+ mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
+ mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
+
+ use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
+ }
+
+ mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH;
+ }
+
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ mr->vert_len = mr->me->totvert;
+ mr->edge_len = mr->me->totedge;
+ mr->loop_len = mr->me->totloop;
+ mr->poly_len = mr->me->totpoly;
+ mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
+
+ mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT);
+ mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE);
+ mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP);
+ mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY);
+
+ mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
+ mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
+ mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
+ }
+ else {
+ /* #BMesh */
+ BMesh *bm = mr->bm;
+
+ mr->vert_len = bm->totvert;
+ mr->edge_len = bm->totedge;
+ mr->loop_len = bm->totloop;
+ mr->poly_len = bm->totface;
+ mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
+ }
+
+ if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
+ mesh_render_data_loose_geom_ensure(mr, cache);
+ mesh_render_data_loose_geom_load(mr, cache);
+ }
+
+ return mr;
+}
+
+void mesh_render_data_free(MeshRenderData *mr)
+{
+ MEM_SAFE_FREE(mr->mlooptri);
+ MEM_SAFE_FREE(mr->poly_normals);
+ MEM_SAFE_FREE(mr->loop_normals);
+
+ /* Loose geometry are owned by MeshBufferExtractionCache. */
+ mr->ledges = NULL;
+ mr->lverts = NULL;
+
+ MEM_freeN(mr);
+}
+
+/** \} */ \ No newline at end of file
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index 7b97ce43558..5743f39f7da 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -43,6 +43,10 @@ struct bGPdata;
#include "BKE_mesh_types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Expose via BKE callbacks */
void DRW_mball_batch_cache_dirty_tag(struct MetaBall *mb, int mode);
void DRW_mball_batch_cache_validate(struct MetaBall *mb);
@@ -262,3 +266,6 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(struct Object *
struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(struct Object *object,
struct ParticleSystem *psys,
struct PTCacheEdit *edit);
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.cc
index e9558fb320c..9ca452cdacc 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.c
+++ b/source/blender/draw/intern/draw_cache_impl_curve.cc
@@ -25,8 +25,11 @@
#include "MEM_guardedalloc.h"
+#include "BLI_array.hh"
+#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
+#include "BLI_span.hh"
#include "BLI_utildefines.h"
#include "DNA_curve_types.h"
@@ -34,6 +37,8 @@
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_font.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_spline.hh"
#include "GPU_batch.h"
#include "GPU_capabilities.h"
@@ -48,6 +53,11 @@
#include "draw_cache_impl.h" /* own include */
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::Span;
+
/* See: edit_curve_point_vert.glsl for duplicate includes. */
#define SELECT 1
#define ACTIVE_NURB (1 << 2)
@@ -139,12 +149,28 @@ static void curve_render_wire_verts_edges_len_get(const CurveCache *ob_curve_cac
}
}
+static void curve_eval_render_wire_verts_edges_len_get(const CurveEval &curve_eval,
+ int *r_curve_len,
+ int *r_vert_len,
+ int *r_edge_len)
+{
+ Span<SplinePtr> splines = curve_eval.splines();
+ *r_curve_len = splines.size();
+ *r_vert_len = 0;
+ *r_edge_len = 0;
+ for (const SplinePtr &spline : splines) {
+ *r_vert_len += spline->evaluated_points_size();
+ *r_edge_len += spline->evaluated_edges_size();
+ }
+}
+
static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_curve_cache)
{
int normal_len = 0;
const BevList *bl;
const Nurb *nu;
- for (bl = ob_curve_cache->bev.first, nu = lb->first; nu && bl; bl = bl->next, nu = nu->next) {
+ for (bl = (const BevList *)ob_curve_cache->bev.first, nu = (const Nurb *)lb->first; nu && bl;
+ bl = bl->next, nu = nu->next) {
int nr = bl->nr;
int skip = nu->resolu / 16;
#if 0
@@ -163,7 +189,7 @@ static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_
/* ---------------------------------------------------------------------- */
/* Curve Interface, indirect, partially cached access to complex data. */
-typedef struct CurveRenderData {
+struct CurveRenderData {
int types;
struct {
@@ -191,6 +217,9 @@ typedef struct CurveRenderData {
/* borrow from 'Object' */
CurveCache *ob_curve_cache;
+ /* Owned by the evaluated object's geometry set (#geometry_set_eval). */
+ const CurveEval *curve_eval;
+
/* borrow from 'Curve' */
ListBase *nurbs;
@@ -198,7 +227,7 @@ typedef struct CurveRenderData {
int actnu;
/* edit, index in active nurb (BPoint or BezTriple) */
int actvert;
-} CurveRenderData;
+};
enum {
/* Wire center-line */
@@ -220,7 +249,7 @@ static CurveRenderData *curve_render_data_create(Curve *cu,
CurveCache *ob_curve_cache,
const int types)
{
- CurveRenderData *rdata = MEM_callocN(sizeof(*rdata), __func__);
+ CurveRenderData *rdata = (CurveRenderData *)MEM_callocN(sizeof(*rdata), __func__);
rdata->types = types;
ListBase *nurbs;
@@ -229,11 +258,21 @@ static CurveRenderData *curve_render_data_create(Curve *cu,
rdata->ob_curve_cache = ob_curve_cache;
+ rdata->curve_eval = cu->curve_eval;
+
if (types & CU_DATATYPE_WIRE) {
- curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache,
- &rdata->wire.curve_len,
- &rdata->wire.vert_len,
- &rdata->wire.edge_len);
+ if (rdata->curve_eval != nullptr) {
+ curve_eval_render_wire_verts_edges_len_get(*rdata->curve_eval,
+ &rdata->wire.curve_len,
+ &rdata->wire.vert_len,
+ &rdata->wire.edge_len);
+ }
+ else {
+ curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache,
+ &rdata->wire.curve_len,
+ &rdata->wire.vert_len,
+ &rdata->wire.edge_len);
+ }
}
if (cu->editnurb) {
@@ -314,7 +353,7 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers,
{
for (int i = 0; i < gpumat_array_len; i++) {
struct GPUMaterial *gpumat = gpumat_array[i];
- if (gpumat == NULL) {
+ if (gpumat == nullptr) {
continue;
}
@@ -354,7 +393,7 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers,
/* ---------------------------------------------------------------------- */
/* Curve GPUBatch Cache */
-typedef struct CurveBatchCache {
+struct CurveBatchCache {
struct {
GPUVertBuf *pos_nor;
GPUVertBuf *edge_fac;
@@ -406,15 +445,15 @@ typedef struct CurveBatchCache {
/* Valid only if edge_detection is up to date. */
bool is_manifold;
-} CurveBatchCache;
+};
/* GPUBatch cache management. */
static bool curve_batch_cache_valid(Curve *cu)
{
- CurveBatchCache *cache = cu->batch_cache;
+ CurveBatchCache *cache = (CurveBatchCache *)cu->batch_cache;
- if (cache == NULL) {
+ if (cache == nullptr) {
return false;
}
@@ -426,7 +465,7 @@ static bool curve_batch_cache_valid(Curve *cu)
return false;
}
- if (cache->is_editmode != ((cu->editnurb != NULL) || (cu->editfont != NULL))) {
+ if (cache->is_editmode != ((cu->editnurb != nullptr) || (cu->editfont != nullptr))) {
return false;
}
@@ -441,10 +480,11 @@ static bool curve_batch_cache_valid(Curve *cu)
static void curve_batch_cache_init(Curve *cu)
{
- CurveBatchCache *cache = cu->batch_cache;
+ CurveBatchCache *cache = (CurveBatchCache *)cu->batch_cache;
if (!cache) {
- cache = cu->batch_cache = MEM_callocN(sizeof(*cache), __func__);
+ cache = (CurveBatchCache *)MEM_callocN(sizeof(*cache), __func__);
+ cu->batch_cache = cache;
}
else {
memset(cache, 0, sizeof(*cache));
@@ -463,11 +503,12 @@ static void curve_batch_cache_init(Curve *cu)
cache->cd_used = 0;
cache->mat_len = DRW_curve_material_count_get(cu);
- cache->surf_per_mat_tris = MEM_callocN(sizeof(*cache->surf_per_mat_tris) * cache->mat_len,
- __func__);
- cache->surf_per_mat = MEM_callocN(sizeof(*cache->surf_per_mat) * cache->mat_len, __func__);
+ cache->surf_per_mat_tris = (GPUIndexBuf **)MEM_callocN(
+ sizeof(*cache->surf_per_mat_tris) * cache->mat_len, __func__);
+ cache->surf_per_mat = (GPUBatch **)MEM_callocN(sizeof(*cache->surf_per_mat) * cache->mat_len,
+ __func__);
- cache->is_editmode = (cu->editnurb != NULL) || (cu->editfont != NULL);
+ cache->is_editmode = (cu->editnurb != nullptr) || (cu->editfont != nullptr);
cache->is_dirty = false;
}
@@ -482,13 +523,13 @@ void DRW_curve_batch_cache_validate(Curve *cu)
static CurveBatchCache *curve_batch_cache_get(Curve *cu)
{
- return cu->batch_cache;
+ return (CurveBatchCache *)cu->batch_cache;
}
void DRW_curve_batch_cache_dirty_tag(Curve *cu, int mode)
{
- CurveBatchCache *cache = cu->batch_cache;
- if (cache == NULL) {
+ CurveBatchCache *cache = (CurveBatchCache *)cu->batch_cache;
+ if (cache == nullptr) {
return;
}
switch (mode) {
@@ -508,7 +549,7 @@ void DRW_curve_batch_cache_dirty_tag(Curve *cu, int mode)
static void curve_batch_cache_clear(Curve *cu)
{
- CurveBatchCache *cache = cu->batch_cache;
+ CurveBatchCache *cache = (CurveBatchCache *)cu->batch_cache;
if (!cache) {
return;
}
@@ -553,8 +594,6 @@ void DRW_curve_batch_cache_free(Curve *cu)
/* GPUBatch cache usage. */
static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos)
{
- BLI_assert(rdata->ob_curve_cache != NULL);
-
static GPUVertFormat format = {0};
static struct {
uint pos;
@@ -567,30 +606,46 @@ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curv
GPU_vertbuf_init_with_format(vbo_curves_pos, &format);
GPU_vertbuf_data_alloc(vbo_curves_pos, vert_len);
- int v_idx = 0;
- LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
- if (bl->nr <= 0) {
- continue;
- }
- const int i_end = v_idx + bl->nr;
- for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) {
- GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec);
+ if (rdata->curve_eval != nullptr) {
+ const CurveEval &curve_eval = *rdata->curve_eval;
+ Span<SplinePtr> splines = curve_eval.splines();
+ Array<int> offsets = curve_eval.evaluated_point_offsets();
+ BLI_assert(offsets.last() == vert_len);
+
+ for (const int i_spline : splines.index_range()) {
+ Span<float3> positions = splines[i_spline]->evaluated_positions();
+ for (const int i_point : positions.index_range()) {
+ GPU_vertbuf_attr_set(
+ vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]);
+ }
}
}
- LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
- if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
- for (int i = 0; i < dl->nr; v_idx++, i++) {
- GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]);
+ else {
+ BLI_assert(rdata->ob_curve_cache != nullptr);
+
+ int v_idx = 0;
+ LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
+ if (bl->nr <= 0) {
+ continue;
+ }
+ const int i_end = v_idx + bl->nr;
+ for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) {
+ GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec);
+ }
+ }
+ LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
+ if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
+ for (int i = 0; i < dl->nr; v_idx++, i++) {
+ GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]);
+ }
}
}
+ BLI_assert(v_idx == vert_len);
}
- BLI_assert(v_idx == vert_len);
}
static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines)
{
- BLI_assert(rdata->ob_curve_cache != NULL);
-
const int vert_len = curve_render_data_wire_verts_len_get(rdata);
const int edge_len = curve_render_data_wire_edges_len_get(rdata);
const int curve_len = curve_render_data_wire_curve_len_get(rdata);
@@ -600,34 +655,56 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c
GPUIndexBufBuilder elb;
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len);
- int v_idx = 0;
- LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
- if (bl->nr <= 0) {
- continue;
- }
- const bool is_cyclic = bl->poly != -1;
- if (is_cyclic) {
- GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1));
- }
- for (int i = 0; i < bl->nr; i++) {
- GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
+ if (rdata->curve_eval != nullptr) {
+ const CurveEval &curve_eval = *rdata->curve_eval;
+ Span<SplinePtr> splines = curve_eval.splines();
+ Array<int> offsets = curve_eval.evaluated_point_offsets();
+ BLI_assert(offsets.last() == vert_len);
+
+ for (const int i_spline : splines.index_range()) {
+ const int eval_size = splines[i_spline]->evaluated_points_size();
+ if (splines[i_spline]->is_cyclic()) {
+ GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1);
+ }
+ for (const int i_point : IndexRange(eval_size)) {
+ GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + i_point);
+ }
+ GPU_indexbuf_add_primitive_restart(&elb);
}
- GPU_indexbuf_add_primitive_restart(&elb);
- v_idx += bl->nr;
}
- LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
- if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
- const bool is_cyclic = dl->type == DL_POLY;
+ else {
+ BLI_assert(rdata->ob_curve_cache != nullptr);
+
+ int v_idx = 0;
+ LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
+ if (bl->nr <= 0) {
+ continue;
+ }
+ const bool is_cyclic = bl->poly != -1;
if (is_cyclic) {
- GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1));
+ GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1));
}
- for (int i = 0; i < dl->nr; i++) {
+ for (int i = 0; i < bl->nr; i++) {
GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
}
GPU_indexbuf_add_primitive_restart(&elb);
- v_idx += dl->nr;
+ v_idx += bl->nr;
+ }
+ LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
+ if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
+ const bool is_cyclic = dl->type == DL_POLY;
+ if (is_cyclic) {
+ GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1));
+ }
+ for (int i = 0; i < dl->nr; i++) {
+ GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
+ }
+ GPU_indexbuf_add_primitive_restart(&elb);
+ v_idx += dl->nr;
+ }
}
}
+
GPU_indexbuf_build_in_place(&elb, ibo_curve_lines);
}
@@ -677,7 +754,9 @@ static void curve_create_edit_curves_nor(CurveRenderData *rdata,
const uint tan_id = do_hq_normals ? attr_id.tan_hq : attr_id.tan;
const uint rad_id = do_hq_normals ? attr_id.rad_hq : attr_id.rad;
- for (bl = rdata->ob_curve_cache->bev.first, nu = rdata->nurbs->first; nu && bl;
+ for (bl = (const BevList *)rdata->ob_curve_cache->bev.first,
+ nu = (const Nurb *)rdata->nurbs->first;
+ nu && bl;
bl = bl->next, nu = nu->next) {
const BevPoint *bevp = bl->bevpoints;
int nr = bl->nr;
@@ -764,6 +843,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2;
int vbo_len_used = 0;
+#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : nullptr))
+#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : nullptr))
+
if (DRW_TEST_ASSIGN_VBO(vbo_pos)) {
GPU_vertbuf_init_with_format(vbo_pos, &format_pos);
GPU_vertbuf_data_alloc(vbo_pos, verts_len_capacity);
@@ -773,8 +855,8 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
GPU_vertbuf_data_alloc(vbo_data, verts_len_capacity);
}
- GPUIndexBufBuilder elb_verts, *elbp_verts = NULL;
- GPUIndexBufBuilder elb_lines, *elbp_lines = NULL;
+ GPUIndexBufBuilder elb_verts, *elbp_verts = nullptr;
+ GPUIndexBufBuilder elb_lines, *elbp_lines = nullptr;
if (DRW_TEST_ASSIGN_IBO(ibo_edit_verts_points)) {
elbp_verts = &elb_verts;
GPU_indexbuf_init(elbp_verts, GPU_PRIM_POINTS, verts_len_capacity, verts_len_capacity);
@@ -784,14 +866,17 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
GPU_indexbuf_init(elbp_lines, GPU_PRIM_LINES, edges_len_capacity, verts_len_capacity);
}
+#undef DRW_TEST_ASSIGN_VBO
+#undef DRW_TEST_ASSIGN_IBO
+
int nu_id = 0;
- for (Nurb *nu = rdata->nurbs->first; nu; nu = nu->next, nu_id++) {
+ for (Nurb *nu = (Nurb *)rdata->nurbs->first; nu; nu = nu->next, nu_id++) {
const BezTriple *bezt = nu->bezt;
const BPoint *bp = nu->bp;
if (bezt) {
for (int a = 0; a < nu->pntsu; a++, bezt++) {
- if (bezt->hide == true) {
+ if (bezt->hide != 0) {
continue;
}
const bool handle_selected = BEZT_ISSEL_ANY(bezt);
@@ -826,7 +911,7 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
else if (bp) {
int pt_len = nu->pntsu * nu->pntsv;
for (int a = 0; a < pt_len; a++, bp++, vbo_len_used += 1) {
- if (bp->hide == true) {
+ if (bp->hide != 0) {
continue;
}
int u = (a % nu->pntsu);
@@ -837,8 +922,8 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
GPU_indexbuf_add_point_vert(elbp_verts, vbo_len_used);
}
if (elbp_lines) {
- const BPoint *bp_next_u = (u < (nu->pntsu - 1)) ? &nu->bp[a + 1] : NULL;
- const BPoint *bp_next_v = (v < (nu->pntsv - 1)) ? &nu->bp[a + nu->pntsu] : NULL;
+ const BPoint *bp_next_u = (u < (nu->pntsu - 1)) ? &nu->bp[a + 1] : nullptr;
+ const BPoint *bp_next_v = (v < (nu->pntsv - 1)) ? &nu->bp[a + nu->pntsu] : nullptr;
if (bp_next_u && (bp_next_u->hide == false)) {
GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used, vbo_len_used + 1);
}
@@ -858,17 +943,17 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
}
/* Resize & Finish */
- if (elbp_verts != NULL) {
+ if (elbp_verts != nullptr) {
GPU_indexbuf_build_in_place(elbp_verts, ibo_edit_verts_points);
}
- if (elbp_lines != NULL) {
+ if (elbp_lines != nullptr) {
GPU_indexbuf_build_in_place(elbp_lines, ibo_edit_lines);
}
if (vbo_len_used != verts_len_capacity) {
- if (vbo_pos != NULL) {
+ if (vbo_pos != nullptr) {
GPU_vertbuf_data_resize(vbo_pos, vbo_len_used);
}
- if (vbo_data != NULL) {
+ if (vbo_data != nullptr) {
GPU_vertbuf_data_resize(vbo_data, vbo_len_used);
}
}
@@ -932,7 +1017,7 @@ GPUVertBuf *DRW_curve_batch_cache_pos_vertbuf_get(struct Curve *cu)
/* Request surface to trigger the vbo filling. Otherwise it may do nothing. */
DRW_batch_request(&cache->batch.surfaces);
- DRW_vbo_request(NULL, &cache->ordered.loop_pos_nor);
+ DRW_vbo_request(nullptr, &cache->ordered.loop_pos_nor);
return cache->ordered.loop_pos_nor;
}
@@ -968,7 +1053,7 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen
{
BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT));
- Curve *cu = ob->data;
+ Curve *cu = (Curve *)ob->data;
CurveBatchCache *cache = curve_batch_cache_get(cu);
/* Verify that all surface batches have needed attribute layers. */
@@ -1065,8 +1150,11 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen
CurveRenderData *rdata = curve_render_data_create(cu, ob->runtime.curve_cache, mr_flag);
- /* DispLists */
- ListBase *lb = &rdata->ob_curve_cache->disp;
+ /* The object's curve cache can be empty (in one case because we use #CurveEval's cache instead),
+ * If so, point to an empty DispList list to avoid the need to check for null in the following
+ * functions. */
+ ListBase empty_lb = {nullptr, nullptr};
+ ListBase *lb = rdata->ob_curve_cache == nullptr ? &empty_lb : &rdata->ob_curve_cache->disp;
/* Generate VBOs */
if (DRW_vbo_requested(cache->ordered.pos_nor)) {
@@ -1118,7 +1206,7 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen
#ifdef DEBUG
/* Make sure all requested batches have been setup. */
for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
- BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0));
+ BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], (GPUPrimType)0));
}
#endif
}
diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c
index d606f70db9e..b59c4db8383 100644
--- a/source/blender/draw/intern/draw_cache_impl_displist.c
+++ b/source/blender/draw/intern/draw_cache_impl_displist.c
@@ -469,7 +469,7 @@ static void displist_surf_fnors_ensure(const DispList *dl, float (**fnors)[3])
{
int u_len = dl->nr - ((dl->flag & DL_CYCL_U) ? 0 : 1);
int v_len = dl->parts - ((dl->flag & DL_CYCL_V) ? 0 : 1);
- const float(*verts)[3] = (float(*)[3])dl->verts;
+ const float(*verts)[3] = (const float(*)[3])dl->verts;
float(*nor_flat)[3] = MEM_mallocN(sizeof(float[3]) * u_len * v_len, __func__);
*fnors = nor_flat;
@@ -532,6 +532,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb,
GPUVertBufRaw uv_step = {0};
GPUVertBufRaw tan_step = {0};
+#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL))
+
if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) {
GPU_vertbuf_init_with_format(vbo_pos_nor,
do_hq_normals ? &format_pos_nor_hq : &format_pos_nor);
@@ -550,13 +552,15 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb,
GPU_vertbuf_attr_get_raw_data(vbo_tan, tan_id, &tan_step);
}
+#undef DRW_TEST_ASSIGN_VBO
+
BKE_displist_normals_add(lb);
LISTBASE_FOREACH (const DispList *, dl, lb) {
const bool is_smooth = (dl->rt & CU_SMOOTH) != 0;
if (ELEM(dl->type, DL_INDEX3, DL_INDEX4, DL_SURF)) {
- const float(*verts)[3] = (float(*)[3])dl->verts;
- const float(*nors)[3] = (float(*)[3])dl->nors;
+ const float(*verts)[3] = (const float(*)[3])dl->verts;
+ const float(*nors)[3] = (const float(*)[3])dl->nors;
const int *idx = dl->index;
float uv[4][2];
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index c07271a0d33..bea9ba1122b 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -348,7 +348,14 @@ static void gpencil_buffer_add_stroke(gpStrokeVert *verts,
}
/* Draw line to first point to complete the loop for cyclic strokes. */
if (is_cyclic) {
- gpencil_buffer_add_point(verts, cols, gps, &pts[0], v++, false);
+ gpencil_buffer_add_point(verts, cols, gps, &pts[0], v, false);
+ /* UV factor needs to be adjusted for the last point to not be equal to the UV factor of the
+ * first point. It should be the factor of the last point plus the distance from the last point
+ * to the first.
+ */
+ gpStrokeVert *vert = &verts[v];
+ vert->u_stroke = verts[v - 1].u_stroke + len_v3v3(&pts[pts_len - 1].x, &pts[0].x);
+ v++;
}
/* Last adjacency point (not drawn). */
adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2);
@@ -398,7 +405,7 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr
if (cache->vbo == NULL) {
/* Should be discarded together. */
BLI_assert(cache->vbo == NULL && cache->ibo == NULL);
- BLI_assert(cache->stroke_batch == NULL && cache->stroke_batch == NULL);
+ BLI_assert(cache->fill_batch == NULL && cache->stroke_batch == NULL);
/* TODO/PERF: Could be changed to only do it if needed.
* For now it's simpler to assume we always need it
* since multiple viewport could or could not need it.
diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c
index fd28ac00186..6424b21666d 100644
--- a/source/blender/draw/intern/draw_cache_impl_hair.c
+++ b/source/blender/draw/intern/draw_cache_impl_hair.c
@@ -243,7 +243,8 @@ static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *c
GPUVertFormat format = {0};
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format);
+ cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format_ex(&format,
+ GPU_USAGE_DEVICE_ONLY);
/* Create a destination buffer for the transform feedback. Sized appropriately */
/* Those are points! not line segments. */
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 04bfb667d24..3cc71e47f28 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -397,7 +397,7 @@ static void drw_mesh_weight_state_extract(Object *ob,
wstate->flags |= DRW_MESH_WEIGHT_STATE_MULTIPAINT |
(ts->auto_normalize ? DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE : 0);
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
BKE_object_defgroup_mirror_selection(ob,
wstate->defgroup_len,
wstate->defgroup_sel,
@@ -557,12 +557,18 @@ static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache)
static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
{
FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.tan);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.orco);
}
+ /* Discard batches using vbo.uv. */
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_area);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_angle);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts);
+
mesh_batch_cache_discard_surface_batches(cache);
mesh_cd_layers_type_clear(&cache->cd_used);
}
@@ -659,8 +665,17 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.lnor);
}
GPU_BATCH_DISCARD_SAFE(cache->batch.surface);
+ /* Discard batches using vbo.pos_nor. */
GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops);
GPU_BATCH_DISCARD_SAFE(cache->batch.wire_edges);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.all_verts);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.all_edges);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.loose_edges);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edge_detection);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.surface_weights);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edit_mesh_analysis);
+ /* Discard batches using vbo.lnor. */
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edit_lnor);
mesh_batch_cache_discard_surface_batches(cache);
cache->batch_ready &= ~(MBC_SURFACE | MBC_WIRE_EDGES | MBC_WIRE_LOOPS);
break;
@@ -692,6 +707,26 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
}
}
+static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache)
+{
+ GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo;
+ GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo;
+ for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) {
+ GPU_VERTBUF_DISCARD_SAFE(vbos[i]);
+ }
+ for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) {
+ GPU_INDEXBUF_DISCARD_SAFE(ibos[i]);
+ }
+}
+
+static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache)
+{
+ MEM_SAFE_FREE(extraction_cache->lverts);
+ MEM_SAFE_FREE(extraction_cache->ledges);
+ extraction_cache->edge_loose_len = 0;
+ extraction_cache->vert_loose_len = 0;
+}
+
static void mesh_batch_cache_clear(Mesh *me)
{
MeshBatchCache *cache = me->runtime.batch_cache;
@@ -699,16 +734,13 @@ static void mesh_batch_cache_clear(Mesh *me)
return;
}
FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo;
- GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo;
- for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) {
- GPU_VERTBUF_DISCARD_SAFE(vbos[i]);
- }
- for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) {
- GPU_INDEXBUF_DISCARD_SAFE(ibos[i]);
- }
+ mesh_buffer_cache_clear(mbufcache);
}
+ mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache);
+ mesh_buffer_extraction_cache_clear(&cache->cage_extraction_cache);
+ mesh_buffer_extraction_cache_clear(&cache->uv_cage_extraction_cache);
+
for (int i = 0; i < cache->mat_len; i++) {
GPU_INDEXBUF_DISCARD_SAFE(cache->final.tris_per_mat[i]);
}
@@ -1145,25 +1177,25 @@ static void drw_mesh_batch_cache_check_available(struct TaskGraph *task_graph, M
* some issues (See T77867 where we needed to disable this function in order to debug what was
* happening in release builds). */
BLI_task_graph_work_and_wait(task_graph);
- for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_BATCH_LEN; i++) {
BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0));
}
- for (int i = 0; i < sizeof(cache->final.vbo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_VBO_LEN; i++) {
BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i]));
}
- for (int i = 0; i < sizeof(cache->final.ibo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_IBO_LEN; i++) {
BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i]));
}
- for (int i = 0; i < sizeof(cache->cage.vbo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_VBO_LEN; i++) {
BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i]));
}
- for (int i = 0; i < sizeof(cache->cage.ibo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_IBO_LEN; i++) {
BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i]));
}
- for (int i = 0; i < sizeof(cache->uv_cage.vbo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_VBO_LEN; i++) {
BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i]));
}
- for (int i = 0; i < sizeof(cache->uv_cage.ibo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_IBO_LEN; i++) {
BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i]));
}
}
@@ -1527,7 +1559,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (do_uvcage) {
mesh_buffer_cache_create_requested(task_graph,
cache,
- cache->uv_cage,
+ &cache->uv_cage,
+ &cache->uv_cage_extraction_cache,
me,
is_editmode,
is_paint_mode,
@@ -1536,7 +1569,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
false,
true,
false,
- &cache->cd_used,
scene,
ts,
true);
@@ -1545,7 +1577,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (do_cage) {
mesh_buffer_cache_create_requested(task_graph,
cache,
- cache->cage,
+ &cache->cage,
+ &cache->cage_extraction_cache,
me,
is_editmode,
is_paint_mode,
@@ -1554,7 +1587,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
false,
false,
use_subsurf_fdots,
- &cache->cd_used,
scene,
ts,
true);
@@ -1562,7 +1594,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
mesh_buffer_cache_create_requested(task_graph,
cache,
- cache->final,
+ &cache->final,
+ &cache->final_extraction_cache,
me,
is_editmode,
is_paint_mode,
@@ -1571,7 +1604,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
true,
false,
use_subsurf_fdots,
- &cache->cd_used,
scene,
ts,
use_hide);
diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c
index 7244dfd12c3..a91a1391c31 100644
--- a/source/blender/draw/intern/draw_cache_impl_volume.c
+++ b/source/blender/draw/intern/draw_cache_impl_volume.c
@@ -152,7 +152,7 @@ typedef struct VolumeWireframeUserData {
} VolumeWireframeUserData;
static void drw_volume_wireframe_cb(
- void *userdata, float (*verts)[3], int (*edges)[2], int totvert, int totedge)
+ void *userdata, const float (*verts)[3], const int (*edges)[2], int totvert, int totedge)
{
VolumeWireframeUserData *data = userdata;
Scene *scene = data->scene;
@@ -225,7 +225,7 @@ GPUBatch *DRW_volume_batch_cache_get_wireframes_face(Volume *volume)
VolumeBatchCache *cache = volume_batch_cache_get(volume);
if (cache->face_wire.batch == NULL) {
- VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume);
+ const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume);
if (volume_grid == NULL) {
return NULL;
}
@@ -274,7 +274,7 @@ GPUBatch *DRW_volume_batch_cache_get_selection_surface(Volume *volume)
{
VolumeBatchCache *cache = volume_batch_cache_get(volume);
if (cache->selection_surface == NULL) {
- VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume);
+ const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume);
if (volume_grid == NULL) {
return NULL;
}
@@ -284,8 +284,8 @@ GPUBatch *DRW_volume_batch_cache_get_selection_surface(Volume *volume)
return cache->selection_surface;
}
-static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
- VolumeGrid *grid,
+static DRWVolumeGrid *volume_grid_cache_get(const Volume *volume,
+ const VolumeGrid *grid,
VolumeBatchCache *cache)
{
const char *name = BKE_volume_grid_name(grid);
@@ -351,7 +351,7 @@ static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
return cache_grid;
}
-DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume, VolumeGrid *volume_grid)
+DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume, const VolumeGrid *volume_grid)
{
VolumeBatchCache *cache = volume_batch_cache_get(volume);
DRWVolumeGrid *grid = volume_grid_cache_get(volume, volume_grid, cache);
diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h
index ebe97b4e7c4..6e537a3bffa 100644
--- a/source/blender/draw/intern/draw_cache_inline.h
+++ b/source/blender/draw/intern/draw_cache_inline.h
@@ -40,10 +40,6 @@
(flag |= DRW_ibo_requested(ibo) ? (value) : 0)
#endif
-/* Test and assign NULL if test fails */
-#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL))
-#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL))
-
BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch)
{
/* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */
@@ -53,13 +49,13 @@ BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch)
return *batch;
}
-BLI_INLINE bool DRW_batch_requested(GPUBatch *batch, int prim_type)
+BLI_INLINE bool DRW_batch_requested(GPUBatch *batch, GPUPrimType prim_type)
{
/* Batch has been requested if it has been created but not initialized. */
if (batch != NULL && batch->verts[0] == NULL) {
/* HACK. We init without a valid VBO and let the first vbo binding
* fill verts[0]. */
- GPU_batch_init_ex(batch, prim_type, (GPUVertBuf *)1, NULL, 0);
+ GPU_batch_init_ex(batch, prim_type, (GPUVertBuf *)1, NULL, (eGPUBatchFlag)0);
batch->verts[0] = NULL;
return true;
}
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index bca227a24e2..585e171adc5 100644
--- a/source/blender/draw/intern/draw_hair.c
+++ b/source/blender/draw/intern/draw_hair.c
@@ -36,15 +36,28 @@
#include "BKE_duplilist.h"
#include "GPU_batch.h"
+#include "GPU_capabilities.h"
+#include "GPU_compute.h"
#include "GPU_shader.h"
+#include "GPU_texture.h"
#include "GPU_vertex_buffer.h"
#include "draw_hair_private.h"
#ifndef __APPLE__
# define USE_TRANSFORM_FEEDBACK
+# define USE_COMPUTE_SHADERS
#endif
+BLI_INLINE bool drw_hair_use_compute_shaders(void)
+{
+#ifdef USE_COMPUTE_SHADERS
+ return GPU_compute_shader_support();
+#else
+ return false;
+#endif
+}
+
typedef enum ParticleRefineShader {
PART_REFINE_CATMULL_ROM = 0,
PART_REFINE_MAX_SHADER,
@@ -71,38 +84,89 @@ static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in t
extern char datatoc_common_hair_lib_glsl[];
extern char datatoc_common_hair_refine_vert_glsl[];
+extern char datatoc_common_hair_refine_comp_glsl[];
extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[];
-static GPUShader *hair_refine_shader_get(ParticleRefineShader sh)
+/* TODO(jbakker): move shader creation to `draw_shaders` and add test cases. */
+/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OS's.
+ * Currently the `__APPLE__` code-path does not compile on other platforms and vice versa. */
+#ifdef USE_COMPUTE_SHADERS
+static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement))
{
- if (g_refine_shaders[sh]) {
- return g_refine_shaders[sh];
- }
-
- char *vert_with_lib = BLI_string_joinN(datatoc_common_hair_lib_glsl,
- datatoc_common_hair_refine_vert_glsl);
+ GPUShader *sh = NULL;
+ sh = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl,
+ datatoc_common_hair_lib_glsl,
+ "#define HAIR_PHASE_SUBDIV\n",
+ __func__);
+ return sh;
+}
+#endif
#ifdef USE_TRANSFORM_FEEDBACK
+static GPUShader *hair_refine_shader_transform_feedback_create(
+ ParticleRefineShader UNUSED(refinement))
+{
+ GPUShader *sh = NULL;
+
+ char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl,
+ datatoc_common_hair_refine_vert_glsl);
const char *var_names[1] = {"finalColor"};
- g_refine_shaders[sh] = DRW_shader_create_with_transform_feedback(
- vert_with_lib, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1);
-#else
- g_refine_shaders[sh] = DRW_shader_create(vert_with_lib,
- NULL,
- datatoc_gpu_shader_3D_smooth_color_frag_glsl,
- "#define blender_srgb_to_framebuffer_space(a) a\n"
- "#define HAIR_PHASE_SUBDIV\n"
- "#define TF_WORKAROUND\n");
+ sh = DRW_shader_create_with_transform_feedback(
+ shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1);
+ MEM_freeN(shader_src);
+
+ return sh;
+}
#endif
- MEM_freeN(vert_with_lib);
+static GPUShader *hair_refine_shader_transform_feedback_workaround_create(
+ ParticleRefineShader UNUSED(refinement))
+{
+ GPUShader *sh = NULL;
+
+ char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl,
+ datatoc_common_hair_refine_vert_glsl);
+ sh = DRW_shader_create(shader_src,
+ NULL,
+ datatoc_gpu_shader_3D_smooth_color_frag_glsl,
+ "#define blender_srgb_to_framebuffer_space(a) a\n"
+ "#define HAIR_PHASE_SUBDIV\n"
+ "#define TF_WORKAROUND\n");
+ MEM_freeN(shader_src);
+
+ return sh;
+}
+
+static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement)
+{
+ if (g_refine_shaders[refinement]) {
+ return g_refine_shaders[refinement];
+ }
+
+#ifdef USE_COMPUTE_SHADERS
+ if (drw_hair_use_compute_shaders()) {
+ g_refine_shaders[refinement] = hair_refine_shader_compute_create(refinement);
+ if (g_refine_shaders[refinement]) {
+ return g_refine_shaders[refinement];
+ }
+ }
+#endif
+
+#ifdef USE_TRANSFORM_FEEDBACK
+ g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_create(refinement);
+ if (g_refine_shaders[refinement]) {
+ return g_refine_shaders[refinement];
+ }
+#endif
- return g_refine_shaders[sh];
+ g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_workaround_create(
+ refinement);
+ return g_refine_shaders[refinement];
}
void DRW_hair_init(void)
{
-#ifdef USE_TRANSFORM_FEEDBACK
+#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS)
g_tf_pass = DRW_pass_create("Update Hair Pass", 0);
#else
g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR);
@@ -125,6 +189,67 @@ void DRW_hair_init(void)
}
}
+static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp,
+ ParticleHairCache *cache,
+ const int subdiv)
+{
+ DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex);
+ DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex);
+ DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex);
+ DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1);
+}
+
+static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, const int subdiv)
+{
+ const int strands_len = cache->strands_len;
+ const int final_points_len = cache->final[subdiv].strands_res * strands_len;
+ if (final_points_len > 0) {
+ GPUShader *shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
+ DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass);
+ drw_hair_particle_cache_shgrp_attach_resources(shgrp, cache, subdiv);
+ DRW_shgroup_vertex_buffer(shgrp, "hairPointOutputBuffer", cache->final[subdiv].proc_buf);
+
+ const int max_strands_per_call = GPU_max_work_group_count(0);
+ int strands_start = 0;
+ while (strands_start < strands_len) {
+ int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call);
+ DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp);
+ DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start);
+ DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1);
+ strands_start += batch_strands_len;
+ }
+ }
+}
+
+static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache *cache,
+ const int subdiv)
+{
+ const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len;
+ if (final_points_len > 0) {
+ GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
+
+#ifdef USE_TRANSFORM_FEEDBACK
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(
+ tf_shader, g_tf_pass, cache->final[subdiv].proc_buf);
+#else
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
+
+ ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__);
+ pr_call->next = g_tf_calls;
+ pr_call->vbo = cache->final[subdiv].proc_buf;
+ pr_call->shgrp = tf_shgrp;
+ pr_call->vert_len = final_points_len;
+ g_tf_calls = pr_call;
+ DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1);
+ DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1);
+ DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1);
+#endif
+
+ drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv);
+ DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len);
+ }
+}
+
static ParticleHairCache *drw_hair_particle_cache_get(
Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res)
{
@@ -140,32 +265,11 @@ static ParticleHairCache *drw_hair_particle_cache_get(
}
if (update) {
- int final_points_len = cache->final[subdiv].strands_res * cache->strands_len;
- if (final_points_len > 0) {
- GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
-
-#ifdef USE_TRANSFORM_FEEDBACK
- DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(
- tf_shader, g_tf_pass, cache->final[subdiv].proc_buf);
-#else
- DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
-
- ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__);
- pr_call->next = g_tf_calls;
- pr_call->vbo = cache->final[subdiv].proc_buf;
- pr_call->shgrp = tf_shgrp;
- pr_call->vert_len = final_points_len;
- g_tf_calls = pr_call;
- DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1);
- DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1);
- DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1);
-#endif
-
- DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", cache->point_tex);
- DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", cache->strand_tex);
- DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandSegBuffer", cache->strand_seg_tex);
- DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1);
- DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len);
+ if (drw_hair_use_compute_shaders()) {
+ drw_hair_particle_cache_update_compute(cache, subdiv);
+ }
+ else {
+ drw_hair_particle_cache_update_transform_feedback(cache, subdiv);
}
}
return cache;
@@ -367,9 +471,11 @@ void DRW_hair_update(void)
MEM_freeN(data);
GPU_framebuffer_free(fb);
#else
- /* TODO(fclem): replace by compute shader. */
- /* Just render using transform feedback. */
+ /* Just render the pass when using compute shaders or transform feedback. */
DRW_draw_pass(g_tf_pass);
+ if (drw_hair_use_compute_shaders()) {
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
#endif
}
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index a088c27d3f3..37f6bbf52b5 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -1473,6 +1473,14 @@ void DRW_draw_callbacks_post_scene(void)
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
+ else {
+ if (v3d && ((v3d->flag2 & V3D_SHOW_ANNOTATION) != 0)) {
+ GPU_depth_test(GPU_DEPTH_NONE);
+ /* XXX: as scene->gpd is not copied for COW yet */
+ ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ }
+ }
}
struct DRWTextStore *DRW_text_cache_ensure(void)
@@ -2601,8 +2609,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph,
void DRW_draw_depth_loop(struct Depsgraph *depsgraph,
ARegion *region,
View3D *v3d,
- GPUViewport *viewport,
- bool use_opengl_context)
+ GPUViewport *viewport)
{
/* Reset before using it. */
drw_state_prepare_clean_for_draw(&DST);
@@ -2618,7 +2625,7 @@ void DRW_draw_depth_loop(struct Depsgraph *depsgraph,
}
}
- drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, use_opengl_context);
+ drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, false);
}
/**
@@ -2634,7 +2641,7 @@ void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph,
use_drw_engine(&draw_engine_gpencil_type);
- drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, true);
+ drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, false);
}
void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect)
@@ -2725,7 +2732,6 @@ void DRW_draw_depth_object(
{
RegionView3D *rv3d = region->regiondata;
- DRW_opengl_context_enable();
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
GPU_matrix_mul(object->obmat);
@@ -2784,7 +2790,6 @@ void DRW_draw_depth_object(
GPU_matrix_set(rv3d->viewmat);
GPU_depth_test(GPU_DEPTH_NONE);
GPU_framebuffer_restore();
- DRW_opengl_context_disable();
}
/** \} */
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index 84bc0327aa2..d4e22c83798 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -187,6 +187,10 @@ typedef enum {
DRW_CMD_DRAW_INSTANCE = 2,
DRW_CMD_DRAW_INSTANCE_RANGE = 3,
DRW_CMD_DRAW_PROCEDURAL = 4,
+
+ /* Compute Commands. */
+ DRW_CMD_COMPUTE = 8,
+
/* Other Commands */
DRW_CMD_CLEAR = 12,
DRW_CMD_DRWSTATE = 13,
@@ -224,6 +228,12 @@ typedef struct DRWCommandDrawInstanceRange {
uint inst_count;
} DRWCommandDrawInstanceRange;
+typedef struct DRWCommandCompute {
+ int groups_x_len;
+ int groups_y_len;
+ int groups_z_len;
+} DRWCommandCompute;
+
typedef struct DRWCommandDrawProcedural {
GPUBatch *batch;
DRWResourceHandle handle;
@@ -260,6 +270,7 @@ typedef union DRWCommand {
DRWCommandDrawInstance instance;
DRWCommandDrawInstanceRange instance_range;
DRWCommandDrawProcedural procedural;
+ DRWCommandCompute compute;
DRWCommandSetMutableState state;
DRWCommandSetStencil stencil;
DRWCommandSetSelectID select_id;
@@ -274,6 +285,7 @@ struct DRWCallBuffer {
};
/** Used by #DRWUniform.type */
+/* TODO(jbakker): rename to DRW_RESOURCE/DRWResourceType. */
typedef enum {
DRW_UNIFORM_INT = 0,
DRW_UNIFORM_INT_COPY,
@@ -286,6 +298,7 @@ typedef enum {
DRW_UNIFORM_BLOCK,
DRW_UNIFORM_BLOCK_REF,
DRW_UNIFORM_TFEEDBACK_TARGET,
+ DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE,
/** Per drawcall uniforms/UBO */
DRW_UNIFORM_BLOCK_OBMATS,
DRW_UNIFORM_BLOCK_OBINFOS,
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 6bdc5305fed..3b852e7f8c8 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -47,6 +47,7 @@
#endif
#include "GPU_buffers.h"
+#include "GPU_capabilities.h"
#include "GPU_material.h"
#include "GPU_uniform_buffer.h"
@@ -446,6 +447,19 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup,
}
}
+void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUVertBuf *vertex_buffer)
+{
+ int location = GPU_shader_get_ssbo(shgroup->shader, name);
+ if (location == -1) {
+ BLI_assert(false && "Unable to locate binding of shader storage buffer objects.");
+ return;
+ }
+ drw_shgroup_uniform_create_ex(
+ shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -700,6 +714,17 @@ static void drw_command_draw_intance_range(
cmd->inst_count = count;
}
+static void drw_command_compute(DRWShadingGroup *shgroup,
+ int groups_x_len,
+ int groups_y_len,
+ int groups_z_len)
+{
+ DRWCommandCompute *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE);
+ cmd->groups_x_len = groups_x_len;
+ cmd->groups_y_len = groups_y_len;
+ cmd->groups_z_len = groups_z_len;
+}
+
static void drw_command_draw_procedural(DRWShadingGroup *shgroup,
GPUBatch *batch,
DRWResourceHandle handle,
@@ -815,6 +840,17 @@ void DRW_shgroup_call_instance_range(
drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct);
}
+void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
+ int groups_x_len,
+ int groups_y_len,
+ int groups_z_len)
+{
+ BLI_assert(groups_x_len > 0 && groups_y_len > 0 && groups_z_len > 0);
+ BLI_assert(GPU_compute_shader_support());
+
+ drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len);
+}
+
static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup,
GPUBatch *geom,
Object *ob,
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index 4c8fcb0e016..f29caebeb84 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -29,6 +29,7 @@
#include "BKE_global.h"
+#include "GPU_compute.h"
#include "GPU_platform.h"
#include "GPU_shader.h"
#include "GPU_state.h"
@@ -672,6 +673,9 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
*use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
((GPUVertBuf *)uni->pvalue));
break;
+ case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE:
+ GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location);
+ break;
/* Legacy/Fallback support. */
case DRW_UNIFORM_BASE_INSTANCE:
state->baseinst_loc = uni->location;
@@ -1050,6 +1054,12 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
cmd->instance_range.inst_count,
false);
break;
+ case DRW_CMD_COMPUTE:
+ GPU_compute_dispatch(shgroup->shader,
+ cmd->compute.groups_x_len,
+ cmd->compute.groups_y_len,
+ cmd->compute.groups_z_len);
+ break;
}
}
diff --git a/source/blender/draw/intern/draw_manager_profiling.c b/source/blender/draw/intern/draw_manager_profiling.c
index 9bfc8d98fe4..783ec1b1d7d 100644
--- a/source/blender/draw/intern/draw_manager_profiling.c
+++ b/source/blender/draw/intern/draw_manager_profiling.c
@@ -41,7 +41,7 @@
#define MAX_TIMER_NAME 32
#define MAX_NESTED_TIMER 8
-#define CHUNK_SIZE 8
+#define MIM_RANGE_LEN 8
#define GPU_TIMER_FALLOFF 0.1
typedef struct DRWTimer {
@@ -82,7 +82,7 @@ void DRW_stats_begin(void)
if (DTP.is_recording && DTP.timers == NULL) {
DTP.chunk_count = 1;
- DTP.timer_count = DTP.chunk_count * CHUNK_SIZE;
+ DTP.timer_count = DTP.chunk_count * MIM_RANGE_LEN;
DTP.timers = MEM_callocN(sizeof(DRWTimer) * DTP.timer_count, "DRWTimer stack");
}
else if (!DTP.is_recording && DTP.timers != NULL) {
@@ -99,7 +99,7 @@ static DRWTimer *drw_stats_timer_get(void)
if (UNLIKELY(DTP.timer_increment >= DTP.timer_count)) {
/* Resize the stack. */
DTP.chunk_count++;
- DTP.timer_count = DTP.chunk_count * CHUNK_SIZE;
+ DTP.timer_count = DTP.chunk_count * MIM_RANGE_LEN;
DTP.timers = MEM_recallocN(DTP.timers, sizeof(DRWTimer) * DTP.timer_count);
}
diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index c93cbf16a30..83d0030f89b 100644
--- a/source/blender/draw/intern/draw_manager_shader.c
+++ b/source/blender/draw/intern/draw_manager_shader.c
@@ -396,6 +396,7 @@ GPUShader *DRW_shader_create_with_transform_feedback(const char *vert,
datatoc_gpu_shader_depth_only_frag_glsl,
geom,
NULL,
+ NULL,
defines,
prim_type,
varying_names,
@@ -606,10 +607,10 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c
haystack += 16;
int dep = drw_shader_library_search(lib, haystack);
if (dep == -1) {
- char dbg_name[32];
+ char dbg_name[33];
int i = 0;
- while ((haystack[0] != ')') && (i < 31)) {
- dbg_name[i] = haystack[0];
+ while ((*haystack != ')') && (i < (sizeof(dbg_name) - 2))) {
+ dbg_name[i] = *haystack;
haystack++;
i++;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
new file mode 100644
index 00000000000..565f8834ab1
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
@@ -0,0 +1,392 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_vector.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Triangles Indices
+ * \{ */
+
+struct MeshExtract_EditUvElem_Data {
+ GPUIndexBufBuilder elb;
+ bool sync_selection;
+};
+
+static void extract_edituv_tris_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len);
+ data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+}
+
+BLI_INLINE void edituv_tri_add(
+ MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3)
+{
+ if (!hidden && (data->sync_selection || selected)) {
+ GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3);
+ }
+}
+
+static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
+ BMLoop **elt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ edituv_tri_add(data,
+ BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT),
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]));
+}
+
+static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ edituv_tri_add(data,
+ (mp->flag & ME_HIDE) != 0,
+ (mp->flag & ME_FACE_SEL) != 0,
+ mlt->tri[0],
+ mlt->tri[1],
+ mlt->tri[2]);
+}
+
+static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_edituv_tris()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_edituv_tris_init;
+ extractor.iter_looptri_bm = extract_edituv_tris_iter_looptri_bm;
+ extractor.iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh;
+ extractor.finish = extract_edituv_tris_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Line Indices around faces
+ * \{ */
+
+static void extract_edituv_lines_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len);
+ data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+}
+
+BLI_INLINE void edituv_edge_add(
+ MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2)
+{
+ if (!hidden && (data->sync_selection || selected)) {
+ GPU_indexbuf_add_line_verts(&data->elb, v1, v2);
+ }
+}
+
+static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ edituv_edge_add(data,
+ BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test_bool(f, BM_ELEM_SELECT),
+ l_index,
+ BM_elem_index_get(l_iter->next));
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ const bool real_edge = (mr->e_origindex == nullptr ||
+ mr->e_origindex[ml->e] != ORIGINDEX_NONE);
+ edituv_edge_add(data,
+ (mp->flag & ME_HIDE) != 0 || !real_edge,
+ (mp->flag & ME_FACE_SEL) != 0,
+ ml_index,
+ ml_index_next);
+ }
+}
+
+static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_edituv_lines()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_edituv_lines_init;
+ extractor.iter_poly_bm = extract_edituv_lines_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_edituv_lines_iter_poly_mesh;
+ extractor.finish = extract_edituv_lines_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Points Indices
+ * \{ */
+
+static void extract_edituv_points_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len);
+ data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+}
+
+BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data,
+ bool hidden,
+ bool selected,
+ int v1)
+{
+ if (!hidden && (data->sync_selection || selected)) {
+ GPU_indexbuf_add_point_vert(&data->elb, v1);
+ }
+}
+
+static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ edituv_point_add(
+ data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
+ mr->v_origindex[ml->v] != ORIGINDEX_NONE);
+ edituv_point_add(
+ data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index);
+ }
+}
+
+static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_edituv_points()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_edituv_points_init;
+ extractor.iter_poly_bm = extract_edituv_points_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_edituv_points_iter_poly_mesh;
+ extractor.finish = extract_edituv_points_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Face-dots Indices
+ * \{ */
+
+static void extract_edituv_fdots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
+ data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+}
+
+BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data,
+ bool hidden,
+ bool selected,
+ int face_index)
+{
+ if (!hidden && (data->sync_selection || selected)) {
+ GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(&data->elb, face_index);
+ }
+}
+
+static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int f_index,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ edituv_facedot_add(data,
+ BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test_bool(f, BM_ELEM_SELECT),
+ f_index);
+}
+
+static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ if (mr->use_subsurf_fdots) {
+ /* Check #ME_VERT_FACEDOT. */
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[mp_index] != ORIGINDEX_NONE);
+ const bool subd_fdot = (!mr->use_subsurf_fdots ||
+ (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0);
+ edituv_facedot_add(data,
+ ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
+ (mp->flag & ME_FACE_SEL) != 0,
+ mp_index);
+ }
+ }
+ else {
+ const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[mp_index] != ORIGINDEX_NONE);
+ edituv_facedot_add(
+ data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index);
+ }
+}
+
+static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_edituv_fdots()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_edituv_fdots_init;
+ extractor.iter_poly_bm = extract_edituv_fdots_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh;
+ extractor.finish = extract_edituv_fdots_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots);
+ return extractor;
+}
+
+/** \} */
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_edituv_tris = blender::draw::create_extractor_edituv_tris();
+const MeshExtract extract_edituv_lines = blender::draw::create_extractor_edituv_lines();
+const MeshExtract extract_edituv_points = blender::draw::create_extractor_edituv_points();
+const MeshExtract extract_edituv_fdots = blender::draw::create_extractor_edituv_fdots();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
new file mode 100644
index 00000000000..1d1a000061d
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
@@ -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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_vector.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots Indices
+ * \{ */
+
+static void extract_fdots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *tls_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
+}
+
+static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int f_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_point_vert(elb, f_index, f_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, f_index);
+ }
+}
+
+static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ if (mr->use_subsurf_fdots) {
+ /* Check #ME_VERT_FACEDOT. */
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ const MVert *mv = &mr->mvert[ml->v];
+ if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) {
+ GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
+ return;
+ }
+ }
+ GPU_indexbuf_set_point_restart(elb, mp_index);
+ }
+ else {
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, mp_index);
+ }
+ }
+}
+
+static void extract_fdots_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_fdots()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_fdots_init;
+ extractor.iter_poly_bm = extract_fdots_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_fdots_iter_poly_mesh;
+ extractor.finish = extract_fdots_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots);
+ return extractor;
+}
+
+/** \} */
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_fdots = blender::draw::create_extractor_fdots();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
new file mode 100644
index 00000000000..64aaed6600f
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
@@ -0,0 +1,256 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edges Indices
+ * \{ */
+
+static void extract_lines_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *tls_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ /* Put loose edges at the end. */
+ GPU_indexbuf_init(
+ elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len);
+}
+
+static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ BMLoop *l_iter, *l_first;
+ /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev;
+ do {
+ if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_line_verts(elb,
+ BM_elem_index_get(l_iter->e),
+ BM_elem_index_get(l_iter),
+ BM_elem_index_get(l_iter->next));
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e));
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_lines_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ /* Using poly & loop iterator would complicate accessing the adjacent loop. */
+ const MLoop *mloop = mr->mloop;
+ const MEdge *medge = mr->medge;
+ if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != nullptr)) {
+ const int ml_index_last = mp->loopstart + (mp->totloop - 1);
+ int ml_index = ml_index_last, ml_index_next = mp->loopstart;
+ do {
+ const MLoop *ml = &mloop[ml_index];
+ const MEdge *med = &medge[ml->e];
+ if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) {
+ GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, ml->e);
+ }
+ } while ((ml_index = ml_index_next++) != ml_index_last);
+ }
+ else {
+ const int ml_index_last = mp->loopstart + (mp->totloop - 1);
+ int ml_index = ml_index_last, ml_index_next = mp->loopstart;
+ do {
+ const MLoop *ml = &mloop[ml_index];
+ GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
+ } while ((ml_index = ml_index_next++) != ml_index_last);
+ }
+}
+
+static void extract_lines_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ const int l_index_offset = mr->edge_len + ledge_index;
+ if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+ const int l_index = mr->loop_len + ledge_index * 2;
+ GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, l_index_offset);
+ }
+ /* Don't render the edge twice. */
+ GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed));
+}
+
+static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ const int l_index_offset = mr->edge_len + ledge_index;
+ const int e_index = mr->ledges[ledge_index];
+ if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
+ const int l_index = mr->loop_len + ledge_index * 2;
+ GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, l_index_offset);
+ }
+ /* Don't render the edge twice. */
+ GPU_indexbuf_set_line_restart(elb, e_index);
+}
+
+static void extract_lines_task_reduce(void *_userdata_to, void *_userdata_from)
+{
+ GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
+ GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
+ GPU_indexbuf_join(elb_to, elb_from);
+}
+
+static void extract_lines_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_lines()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_init;
+ extractor.iter_poly_bm = extract_lines_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_lines_iter_poly_mesh;
+ extractor.iter_ledge_bm = extract_lines_iter_ledge_bm;
+ extractor.iter_ledge_mesh = extract_lines_iter_ledge_mesh;
+ extractor.task_reduce = extract_lines_task_reduce;
+ extractor.finish = extract_lines_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = true;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Lines and Loose Edges Sub Buffer
+ * \{ */
+
+static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache)
+{
+ BLI_assert(cache->final.ibo.lines);
+ /* Multiply by 2 because these are edges indices. */
+ const int start = mr->edge_len * 2;
+ const int len = mr->edge_loose_len * 2;
+ GPU_indexbuf_create_subrange_in_place(
+ cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len);
+ cache->no_loose_wire = (len == 0);
+}
+
+static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(elb, ibo);
+ extract_lines_loose_subbuffer(mr, cache);
+}
+
+constexpr MeshExtract create_extractor_lines_with_lines_loose()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_init;
+ extractor.iter_poly_bm = extract_lines_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_lines_iter_poly_mesh;
+ extractor.iter_ledge_bm = extract_lines_iter_ledge_bm;
+ extractor.iter_ledge_mesh = extract_lines_iter_ledge_mesh;
+ extractor.task_reduce = extract_lines_task_reduce;
+ extractor.finish = extract_lines_with_lines_loose_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = true;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loose Edges Sub Buffer
+ * \{ */
+
+static void extract_lines_loose_only_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ BLI_assert(buf == cache->final.ibo.lines_loose);
+ UNUSED_VARS_NDEBUG(buf);
+ extract_lines_loose_subbuffer(mr, cache);
+}
+
+constexpr MeshExtract create_extractor_lines_loose_only()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_loose_only_init;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = 0;
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose);
+ return extractor;
+}
+
+/** \} */
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_lines = blender::draw::create_extractor_lines();
+const MeshExtract extract_lines_with_lines_loose =
+ blender::draw::create_extractor_lines_with_lines_loose();
+const MeshExtract extract_lines_loose_only = blender::draw::create_extractor_lines_loose_only();
+}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
new file mode 100644
index 00000000000..cace18b0c08
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
@@ -0,0 +1,198 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_edgehash.h"
+#include "BLI_vector.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Line Adjacency Indices
+ * \{ */
+
+#define NO_EDGE INT_MAX
+
+struct MeshExtract_LineAdjacency_Data {
+ GPUIndexBufBuilder elb;
+ EdgeHash *eh;
+ bool is_manifold;
+ /* Array to convert vert index to any loop index of this vert. */
+ uint *vert_to_loop;
+};
+
+static void extract_lines_adjacency_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *tls_data)
+{
+ /* Similar to poly_to_tri_count().
+ * There is always (loop + triangle - 1) edges inside a polygon.
+ * Accumulate for all polys and you get : */
+ uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len;
+
+ MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(tls_data);
+ data->vert_to_loop = static_cast<uint *>(MEM_callocN(sizeof(uint) * mr->vert_len, __func__));
+
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len);
+ data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len);
+ data->is_manifold = true;
+}
+
+BLI_INLINE void lines_adjacency_triangle(
+ uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data)
+{
+ GPUIndexBufBuilder *elb = &data->elb;
+ /* Iterate around the triangle's edges. */
+ for (int e = 0; e < 3; e++) {
+ SHIFT3(uint, v3, v2, v1);
+ SHIFT3(uint, l3, l2, l1);
+
+ bool inv_indices = (v2 > v3);
+ void **pval;
+ bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval);
+ int v_data = POINTER_AS_INT(*pval);
+ if (!value_is_init || v_data == NO_EDGE) {
+ /* Save the winding order inside the sign bit. Because the
+ * Edge-hash sort the keys and we need to compare winding later. */
+ int value = (int)l1 + 1; /* 0 cannot be signed so add one. */
+ *pval = POINTER_FROM_INT((inv_indices) ? -value : value);
+ /* Store loop indices for remaining non-manifold edges. */
+ data->vert_to_loop[v2] = l2;
+ data->vert_to_loop[v3] = l3;
+ }
+ else {
+ /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */
+ *pval = POINTER_FROM_INT(NO_EDGE);
+ bool inv_opposite = (v_data < 0);
+ uint l_opposite = (uint)abs(v_data) - 1;
+ /* TODO Make this part thread-safe. */
+ if (inv_opposite == inv_indices) {
+ /* Don't share edge if triangles have non matching winding. */
+ GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1);
+ GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite);
+ data->is_manifold = false;
+ }
+ else {
+ GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite);
+ }
+ }
+ }
+}
+
+static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
+ BMLoop **elt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data);
+ if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+ lines_adjacency_triangle(BM_elem_index_get(elt[0]->v),
+ BM_elem_index_get(elt[1]->v),
+ BM_elem_index_get(elt[2]->v),
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]),
+ data);
+ }
+}
+
+static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data);
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
+ mr->mloop[mlt->tri[1]].v,
+ mr->mloop[mlt->tri[2]].v,
+ mlt->tri[0],
+ mlt->tri[1],
+ mlt->tri[2],
+ data);
+ }
+}
+
+static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *_data)
+{
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data);
+ /* Create edges for remaining non manifold edges. */
+ EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh);
+ for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+ uint v2, v3, l1, l2, l3;
+ int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
+ if (v_data != NO_EDGE) {
+ BLI_edgehashIterator_getKey(ehi, &v2, &v3);
+ l1 = (uint)abs(v_data) - 1;
+ if (v_data < 0) { /* inv_opposite */
+ SWAP(uint, v2, v3);
+ }
+ l2 = data->vert_to_loop[v2];
+ l3 = data->vert_to_loop[v3];
+ GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1);
+ data->is_manifold = false;
+ }
+ }
+ BLI_edgehashIterator_free(ehi);
+ BLI_edgehash_free(data->eh, nullptr);
+
+ cache->is_manifold = data->is_manifold;
+
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+ MEM_freeN(data->vert_to_loop);
+}
+
+#undef NO_EDGE
+
+/** \} */
+
+constexpr MeshExtract create_extractor_lines_adjacency()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_adjacency_init;
+ extractor.iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm;
+ extractor.iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh;
+ extractor.finish = extract_lines_adjacency_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_LineAdjacency_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency);
+ return extractor;
+}
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_lines_adjacency = blender::draw::create_extractor_lines_adjacency();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
new file mode 100644
index 00000000000..a142692ab4e
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
@@ -0,0 +1,127 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_vector.hh"
+#include "atomic_ops.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+/* ---------------------------------------------------------------------- */
+/** \name Extract Paint Mask Line Indices
+ * \{ */
+
+struct MeshExtract_LinePaintMask_Data {
+ GPUIndexBufBuilder elb;
+ /** One bit per edge set if face is selected. */
+ BLI_bitmap *select_map;
+};
+
+static void extract_lines_paint_mask_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(tls_data);
+ data->select_map = BLI_BITMAP_NEW(mr->edge_len, __func__);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len);
+}
+
+static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(_data);
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ const int e_index = ml->e;
+ const MEdge *me = &mr->medge[e_index];
+ if (!((mr->use_hide && (me->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
+
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ if (mp->flag & ME_FACE_SEL) {
+ if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) {
+ /* Hide edge as it has more than 2 selected loop. */
+ GPU_indexbuf_set_line_restart(&data->elb, e_index);
+ }
+ else {
+ /* First selected loop. Set edge visible, overwriting any unselected loop. */
+ GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
+ }
+ }
+ else {
+ /* Set these unselected loop only if this edge has no other selected loop. */
+ if (!BLI_BITMAP_TEST(data->select_map, e_index)) {
+ GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
+ }
+ }
+ }
+ else {
+ GPU_indexbuf_set_line_restart(&data->elb, e_index);
+ }
+ }
+}
+
+static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+ MEM_freeN(data->select_map);
+}
+
+/** \} */
+
+constexpr MeshExtract create_extractor_lines_paint_mask()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_paint_mask_init;
+ extractor.iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh;
+ extractor.finish = extract_lines_paint_mask_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_LinePaintMask_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask);
+ return extractor;
+}
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_lines_paint_mask = blender::draw::create_extractor_lines_paint_mask();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
new file mode 100644
index 00000000000..1e4e76ba7c5
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
@@ -0,0 +1,182 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_vector.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Point Indices
+ * \{ */
+static void extract_points_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *tls_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len);
+}
+
+BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, const BMVert *eve, int l_index)
+{
+ const int v_index = BM_elem_index_get(eve);
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_point_vert(elb, v_index, l_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, v_index);
+ }
+}
+
+BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
+ const MeshRenderData *mr,
+ const int v_index,
+ const int l_index)
+{
+ const MVert *mv = &mr->mvert[v_index];
+ if (!((mr->use_hide && (mv->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
+ (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) {
+ GPU_indexbuf_set_point_vert(elb, v_index, l_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, v_index);
+ }
+}
+
+static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ vert_set_bm(elb, l_iter->v, l_index);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_points_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ vert_set_mesh(elb, mr, ml->v, ml_index);
+ }
+}
+
+static void extract_points_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2));
+ vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1);
+}
+
+static void extract_points_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2));
+ vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1);
+}
+
+static void extract_points_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ vert_set_bm(elb, eve, offset + lvert_index);
+}
+
+static void extract_points_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index);
+}
+
+static void extract_points_task_reduce(void *_userdata_to, void *_userdata_from)
+{
+ GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
+ GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
+ GPU_indexbuf_join(elb_to, elb_from);
+}
+
+static void extract_points_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_points()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_points_init;
+ extractor.iter_poly_bm = extract_points_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_points_iter_poly_mesh;
+ extractor.iter_ledge_bm = extract_points_iter_ledge_bm;
+ extractor.iter_ledge_mesh = extract_points_iter_ledge_mesh;
+ extractor.iter_lvert_bm = extract_points_iter_lvert_bm;
+ extractor.iter_lvert_mesh = extract_points_iter_lvert_mesh;
+ extractor.task_reduce = extract_points_task_reduce;
+ extractor.finish = extract_points_finish;
+ extractor.use_threading = true;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points);
+ return extractor;
+}
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_points = blender::draw::create_extractor_points();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
new file mode 100644
index 00000000000..70b46481b51
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
@@ -0,0 +1,269 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Triangles Indices (multi material)
+ * \{ */
+
+struct MeshExtract_Tri_Data {
+ GPUIndexBufBuilder elb;
+ int *tri_mat_start;
+ int *tri_mat_end;
+};
+
+static void extract_tris_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(tls_data);
+
+ size_t mat_tri_idx_size = sizeof(int) * mr->mat_len;
+ data->tri_mat_start = static_cast<int *>(MEM_callocN(mat_tri_idx_size, __func__));
+ data->tri_mat_end = static_cast<int *>(MEM_callocN(mat_tri_idx_size, __func__));
+
+ int *mat_tri_len = data->tri_mat_start;
+ /* Count how many triangle for each material. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) {
+ if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ int mat = min_ii(efa->mat_nr, mr->mat_len - 1);
+ mat_tri_len[mat] += efa->len - 2;
+ }
+ }
+ }
+ else {
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
+ mat_tri_len[mat] += mp->totloop - 2;
+ }
+ }
+ }
+ /* Accumulate triangle lengths per material to have correct offsets. */
+ int ofs = mat_tri_len[0];
+ mat_tri_len[0] = 0;
+ for (int i = 1; i < mr->mat_len; i++) {
+ int tmp = mat_tri_len[i];
+ mat_tri_len[i] = ofs;
+ ofs += tmp;
+ }
+
+ memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size);
+
+ int visible_tri_tot = ofs;
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len);
+}
+
+static void extract_tris_iter_looptri_bm(const MeshRenderData *mr,
+ BMLoop **elt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
+ const int mat_last = mr->mat_len - 1;
+
+ if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+ int *mat_tri_ofs = data->tri_mat_end;
+ const int mat = min_ii(elt[0]->f->mat_nr, mat_last);
+ GPU_indexbuf_set_tri_verts(&data->elb,
+ mat_tri_ofs[mat]++,
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]));
+ }
+}
+
+static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
+ const int mat_last = mr->mat_len - 1;
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ int *mat_tri_ofs = data->tri_mat_end;
+ const int mat = min_ii(mp->mat_nr, mat_last);
+ GPU_indexbuf_set_tri_verts(
+ &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+ }
+}
+
+static void extract_tris_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *_data)
+{
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+
+ /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
+ * is created before the surfaces-per-material. */
+ if (mr->use_final_mesh && cache->final.tris_per_mat) {
+ MeshBufferCache *mbc_final = &cache->final;
+ for (int i = 0; i < mr->mat_len; i++) {
+ /* These IBOs have not been queried yet but we create them just in case they are needed
+ * later since they are not tracked by mesh_buffer_cache_create_requested(). */
+ if (mbc_final->tris_per_mat[i] == nullptr) {
+ mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc();
+ }
+ /* Multiply by 3 because these are triangle indices. */
+ const int mat_start = data->tri_mat_start[i];
+ const int mat_end = data->tri_mat_end[i];
+ const int start = mat_start * 3;
+ const int len = (mat_end - mat_start) * 3;
+ GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len);
+ }
+ }
+ MEM_freeN(data->tri_mat_start);
+ MEM_freeN(data->tri_mat_end);
+}
+
+constexpr MeshExtract create_extractor_tris()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_tris_init;
+ extractor.iter_looptri_bm = extract_tris_iter_looptri_bm;
+ extractor.iter_looptri_mesh = extract_tris_iter_looptri_mesh;
+ extractor.finish = extract_tris_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_Tri_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris);
+ return extractor;
+}
+
+/** \} */
+
+/** \name Extract Triangles Indices (single material)
+ * \{ */
+
+static void extract_tris_single_mat_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len);
+}
+
+static void extract_tris_single_mat_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
+ BMLoop **elt,
+ const int elt_index,
+ void *_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_tri_verts(elb,
+ elt_index,
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]));
+ }
+ else {
+ GPU_indexbuf_set_tri_restart(elb, elt_index);
+ }
+}
+
+static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int mlt_index,
+ void *_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+ }
+ else {
+ GPU_indexbuf_set_tri_restart(elb, mlt_index);
+ }
+}
+
+static void extract_tris_single_mat_task_reduce(void *_userdata_to, void *_userdata_from)
+{
+ GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
+ GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
+ GPU_indexbuf_join(elb_to, elb_from);
+}
+
+static void extract_tris_single_mat_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *_data)
+{
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ GPU_indexbuf_build_in_place(elb, ibo);
+
+ /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
+ * is created before the surfaces-per-material. */
+ if (mr->use_final_mesh && cache->final.tris_per_mat) {
+ MeshBufferCache *mbc = &cache->final;
+ for (int i = 0; i < mr->mat_len; i++) {
+ /* These IBOs have not been queried yet but we create them just in case they are needed
+ * later since they are not tracked by mesh_buffer_cache_create_requested(). */
+ if (mbc->tris_per_mat[i] == NULL) {
+ mbc->tris_per_mat[i] = GPU_indexbuf_calloc();
+ }
+ /* Multiply by 3 because these are triangle indices. */
+ const int len = mr->tri_len * 3;
+ GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, 0, len);
+ }
+ }
+}
+
+constexpr MeshExtract create_extractor_tris_single_mat()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_tris_single_mat_init;
+ extractor.iter_looptri_bm = extract_tris_single_mat_iter_looptri_bm;
+ extractor.iter_looptri_mesh = extract_tris_single_mat_iter_looptri_mesh;
+ extractor.task_reduce = extract_tris_single_mat_task_reduce;
+ extractor.finish = extract_tris_single_mat_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = true;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris);
+ return extractor;
+}
+
+/** \} */
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_tris = blender::draw::create_extractor_tris();
+const MeshExtract extract_tris_single_mat = blender::draw::create_extractor_tris_single_mat();
+}
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index 8684d82f228..02c335ddae2 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -28,6 +28,9 @@ uniform bool hairCloseTip = true;
uniform vec4 hairDupliMatrix[4];
+/* Strand batch offset when used in compute shaders. */
+uniform int hairStrandOffset = 0;
+
/* -- Per control points -- */
uniform samplerBuffer hairPointBuffer; /* RGBA32F */
#define point_position xyz
@@ -43,13 +46,37 @@ uniform usamplerBuffer hairStrandSegBuffer; /* R16UI */
/* -- Subdivision stage -- */
/**
- * We use a transform feedback to preprocess the strands and add more subdivision to it.
- * For the moment these are simple smooth interpolation but one could hope to see the full
+ * We use a transform feedback or compute shader to preprocess the strands and add more subdivision
+ * to it. For the moment these are simple smooth interpolation but one could hope to see the full
* children particle modifiers being evaluated at this stage.
*
* If no more subdivision is needed, we can skip this step.
*/
+#ifdef GPU_VERTEX_SHADER
+float hair_get_local_time()
+{
+ return float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1);
+}
+
+int hair_get_id()
+{
+ return gl_VertexID / hairStrandsRes;
+}
+#endif
+
+#ifdef GPU_COMPUTE_SHADER
+float hair_get_local_time()
+{
+ return float(gl_GlobalInvocationID.y) / float(hairStrandsRes - 1);
+}
+
+int hair_get_id()
+{
+ return int(gl_GlobalInvocationID.x) + hairStrandOffset;
+}
+#endif
+
#ifdef HAIR_PHASE_SUBDIV
int hair_get_base_id(float local_time, int strand_segments, out float interp_time)
{
@@ -64,9 +91,9 @@ int hair_get_base_id(float local_time, int strand_segments, out float interp_tim
void hair_get_interp_attrs(
out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time)
{
- float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1);
+ float local_time = hair_get_local_time();
- int hair_id = gl_VertexID / hairStrandsRes;
+ int hair_id = hair_get_id();
int strand_offset = int(texelFetch(hairStrandBuffer, hair_id).x);
int strand_segments = int(texelFetch(hairStrandSegBuffer, hair_id).x);
@@ -96,6 +123,7 @@ void hair_get_interp_attrs(
*/
#if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER)
+
int hair_get_strand_id(void)
{
return gl_VertexID / (hairStrandsRes * hairThicknessRes);
@@ -227,3 +255,45 @@ vec2 hair_resolve_barycentric(vec2 vert_barycentric)
return vec2(1.0 - vert_barycentric.x, 0.0);
}
}
+
+/* Hair interpolation functions. */
+vec4 hair_get_weights_cardinal(float t)
+{
+ float t2 = t * t;
+ float t3 = t2 * t;
+#if defined(CARDINAL)
+ float fc = 0.71;
+#else /* defined(CATMULL_ROM) */
+ float fc = 0.5;
+#endif
+
+ vec4 weights;
+ /* GLSL Optimized version of key_curve_position_weights() */
+ float fct = t * fc;
+ float fct2 = t2 * fc;
+ float fct3 = t3 * fc;
+ weights.x = (fct2 * 2.0 - fct3) - fct;
+ weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0;
+ weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct;
+ weights.w = fct3 - fct2;
+ return weights;
+}
+
+/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */
+vec4 hair_get_weights_bspline(float t)
+{
+ float t2 = t * t;
+ float t3 = t2 * t;
+
+ vec4 weights;
+ /* GLSL Optimized version of key_curve_position_weights() */
+ weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666);
+ weights.y = (0.5 * t3 - t2 + 0.66666666);
+ weights.w = (0.16666666 * t3);
+ return weights;
+}
+
+vec4 hair_interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w)
+{
+ return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w;
+}
diff --git a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl
new file mode 100644
index 00000000000..4dcde4b0245
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl
@@ -0,0 +1,24 @@
+
+/*
+ * To be compiled with common_hair_lib.glsl.
+ */
+
+layout(local_size_x = 1, local_size_y = 1) in;
+layout(std430, binding = 0) writeonly buffer hairPointOutputBuffer
+{
+ vec4 posTime[];
+}
+out_vertbuf;
+
+void main(void)
+{
+ float interp_time;
+ vec4 data0, data1, data2, data3;
+ hair_get_interp_attrs(data0, data1, data2, data3, interp_time);
+
+ vec4 weights = hair_get_weights_cardinal(interp_time);
+ vec4 result = hair_interp_data(data0, data1, data2, data3, weights);
+
+ uint index = uint(hair_get_id() * hairStrandsRes) + gl_GlobalInvocationID.y;
+ out_vertbuf.posTime[index] = result;
+}
diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
index 3f5e3f8226f..371d43827b9 100644
--- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
@@ -3,47 +3,6 @@
out vec4 finalColor;
-vec4 get_weights_cardinal(float t)
-{
- float t2 = t * t;
- float t3 = t2 * t;
-#if defined(CARDINAL)
- float fc = 0.71;
-#else /* defined(CATMULL_ROM) */
- float fc = 0.5;
-#endif
-
- vec4 weights;
- /* GLSL Optimized version of key_curve_position_weights() */
- float fct = t * fc;
- float fct2 = t2 * fc;
- float fct3 = t3 * fc;
- weights.x = (fct2 * 2.0 - fct3) - fct;
- weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0;
- weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct;
- weights.w = fct3 - fct2;
- return weights;
-}
-
-/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */
-vec4 get_weights_bspline(float t)
-{
- float t2 = t * t;
- float t3 = t2 * t;
-
- vec4 weights;
- /* GLSL Optimized version of key_curve_position_weights() */
- weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666);
- weights.y = (0.5 * t3 - t2 + 0.66666666);
- weights.w = (0.16666666 * t3);
- return weights;
-}
-
-vec4 interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w)
-{
- return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w;
-}
-
#ifdef TF_WORKAROUND
uniform int targetWidth;
uniform int targetHeight;
@@ -56,8 +15,8 @@ void main(void)
vec4 data0, data1, data2, data3;
hair_get_interp_attrs(data0, data1, data2, data3, interp_time);
- vec4 weights = get_weights_cardinal(interp_time);
- finalColor = interp_data(data0, data1, data2, data3, weights);
+ vec4 weights = hair_get_weights_cardinal(interp_time);
+ finalColor = hair_interp_data(data0, data1, data2, data3, weights);
#ifdef TF_WORKAROUND
int id = gl_VertexID - idOffset;
diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl
index 0344b977139..479f9cd1827 100644
--- a/source/blender/draw/intern/shaders/common_math_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_lib.glsl
@@ -86,11 +86,18 @@ float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; }
vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); }
vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); }
+float safe_sqrt(float a) { return sqrt(max(a, 0.0)); }
+
float sqr(float a) { return a * a; }
vec2 sqr(vec2 a) { return a * a; }
vec3 sqr(vec3 a) { return a * a; }
vec4 sqr(vec4 a) { return a * a; }
+/* Use manual powers for fixed powers. pow() can have unpredicatble results on some implementations.
+ * (see T87369, T87541) */
+float pow6(float x) { return sqr(sqr(x) * x); }
+float pow8(float x) { return sqr(sqr(sqr(x))); }
+
float len_squared(vec3 a) { return dot(a, a); }
float len_squared(vec2 a) { return dot(a, a); }
diff --git a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
index f007a8c2322..74b989441a2 100644
--- a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
@@ -12,7 +12,7 @@ in vec3 nor;
mat3 pointcloud_get_facing_matrix(vec3 p)
{
mat3 facing_mat;
- facing_mat[2] = normalize(ViewMatrixInverse[3].xyz - p);
+ facing_mat[2] = cameraVec(p);
facing_mat[1] = normalize(cross(ViewMatrixInverse[0].xyz, facing_mat[2]));
facing_mat[0] = cross(facing_mat[1], facing_mat[2]);
return facing_mat;
diff --git a/source/blender/draw/tests/draw_testing.cc b/source/blender/draw/tests/draw_testing.cc
new file mode 100644
index 00000000000..0104437e921
--- /dev/null
+++ b/source/blender/draw/tests/draw_testing.cc
@@ -0,0 +1,18 @@
+/* Apache License, Version 2.0 */
+
+#include "draw_testing.hh"
+
+#include "GPU_shader.h"
+
+#include "draw_manager_testing.h"
+
+namespace blender::draw {
+
+/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
+void DrawTest::SetUp()
+{
+ GPUTest::SetUp();
+ DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
+}
+
+} // namespace blender::draw
diff --git a/source/blender/draw/tests/draw_testing.hh b/source/blender/draw/tests/draw_testing.hh
new file mode 100644
index 00000000000..ec0b15b611e
--- /dev/null
+++ b/source/blender/draw/tests/draw_testing.hh
@@ -0,0 +1,13 @@
+/* Apache License, Version 2.0 */
+
+#include "gpu_testing.hh"
+
+namespace blender::draw {
+
+/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
+class DrawTest : public blender::gpu::GPUTest {
+ public:
+ void SetUp() override;
+};
+
+} // namespace blender::draw
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
index 96d544fd855..c96f22859ca 100644
--- a/source/blender/draw/tests/shaders_test.cc
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -2,12 +2,12 @@
#include "testing/testing.h"
+#include "draw_testing.hh"
#include "intern/draw_manager_testing.h"
#include "GPU_context.h"
#include "GPU_init_exit.h"
#include "GPU_shader.h"
-#include "gpu_testing.hh"
#include "engines/eevee/eevee_private.h"
#include "engines/gpencil/gpencil_engine.h"
@@ -17,19 +17,9 @@
namespace blender::draw {
-/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
-class DrawTest : public blender::gpu::GPUTest {
- void SetUp() override
- {
- GPUTest::SetUp();
- DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
- }
-};
-
TEST_F(DrawTest, workbench_glsl_shaders)
{
workbench_shader_library_ensure();
- DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
const int MAX_WPD = 6;
WORKBENCH_PrivateData wpds[MAX_WPD];
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 2bfa417eb78..0b587191807 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -4666,7 +4666,7 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void
}
/* UI updates */
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
/* Tag for full animation update, so that the settings will have an effect. */
@@ -4674,7 +4674,7 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void
DEG_id_tag_update(ale_setting->id, ID_RECALC_ANIMATION);
}
if (ale_setting->adt && ale_setting->adt->action) {
- /* Action is it's own datablock, so has to be tagged specifically. */
+ /* Action is its own datablock, so has to be tagged specifically. */
DEG_id_tag_update(&ale_setting->adt->action->id, ID_RECALC_ANIMATION);
}
@@ -5462,7 +5462,6 @@ void ANIM_channel_draw_widgets(const bContext *C,
prop = RNA_struct_find_property(&ptr, "use_mask_layer");
gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop);
if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) {
- icon = ICON_LAYER_ACTIVE;
if (gpl->flag & GP_LAYER_USE_MASK) {
icon = ICON_MOD_MASK;
}
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 64082b08da9..6c6fab13b7a 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -1349,10 +1349,12 @@ static void join_groups_action_temp(bAction *act)
/* BLI_movelisttolist() doesn't touch first->prev and last->next pointers in its "dst" list.
* Ensure that after the reshuffling the list is properly terminated. */
- FCurve *act_fcurves_first = act->curves.first;
- act_fcurves_first->prev = NULL;
- FCurve *act_fcurves_last = act->curves.last;
- act_fcurves_last->next = NULL;
+ if (!BLI_listbase_is_empty(&act->curves)) {
+ FCurve *act_fcurves_first = act->curves.first;
+ act_fcurves_first->prev = NULL;
+ FCurve *act_fcurves_last = act->curves.last;
+ act_fcurves_last->next = NULL;
+ }
}
/* Change the order of anim-channels within action
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 4cc0413be5b..f229d48b4eb 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -483,7 +483,8 @@ int insert_bezt_fcurve(FCurve *fcu, const BezTriple *bezt, eInsertKeyFlags flag)
return i;
}
-/** Update the FCurve to allow insertion of `bezt` without modifying the curve shape.
+/**
+ * Update the FCurve to allow insertion of `bezt` without modifying the curve shape.
*
* Checks whether it is necessary to apply Bezier subdivision due to involvement of non-auto
* handles. If necessary, changes `bezt` handles from Auto to Aligned.
@@ -3034,19 +3035,9 @@ bool ED_autokeyframe_pchan(
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
BLI_freelistN(&dsources);
- /* clear any unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
-
return true;
}
- /* add unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag |= BONE_UNKEYED;
- }
-
return false;
}
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index 98c050950be..0030e78002b 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -17,6 +17,7 @@
set(INC
../include
+ ../../blenfont
../../blenkernel
../../blenlib
../../blentranslation
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index 68fff1091af..3902f6613a1 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -339,7 +339,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob)
}
BKE_pose_channels_hash_free(ob->pose);
- BKE_pose_channels_hash_make(ob->pose);
+ BKE_pose_channels_hash_ensure(ob->pose);
GHash *name_map = BLI_ghash_str_new(__func__);
@@ -390,7 +390,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
bConstraint *curcon;
ListBase *conlist;
- if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) {
+ if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name))) {
if ((conlist = &pchan->constraints)) {
for (curcon = conlist->first; curcon; curcon = curcon->next) {
/* does this constraint have a subtarget in
@@ -825,7 +825,7 @@ static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig
bConstraint *curcon;
ListBase *conlist;
- if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL ||
+ if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name)) == NULL ||
(conlist = &pchan->constraints) == NULL) {
return;
}
@@ -855,7 +855,7 @@ static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Obj
return;
}
bPoseChannel *pchan;
- pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name);
+ pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name);
if (pchan->custom != NULL) {
Main *bmain = CTX_data_main(C);
@@ -885,12 +885,12 @@ static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, O
if (src_ob->pose) {
bPoseChannel *chanold, *channew;
- chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name);
+ chanold = BKE_pose_channel_ensure(src_ob->pose, src_bone->name);
if (chanold) {
/* WARNING: this creates a new posechannel, but there will not be an attached bone
* yet as the new bones created here are still 'EditBones' not 'Bones'.
*/
- channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name);
+ channew = BKE_pose_channel_ensure(dst_ob->pose, dst_bone->name);
if (channew) {
BKE_pose_channel_copy_data(channew, chanold);
@@ -1193,7 +1193,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
* is synchronized. */
bPoseChannel *pchan;
/* Make sure we clean up the old data before overwriting it */
- pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name);
+ pchan = BKE_pose_channel_ensure(obedit->pose, ebone_iter->temp.ebone->name);
BKE_pose_channel_free(pchan);
/* Sync pchan data */
copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit);
diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c
index 8bcaf72f678..70154695dcd 100644
--- a/source/blender/editors/armature/armature_naming.c
+++ b/source/blender/editors/armature/armature_naming.c
@@ -525,8 +525,8 @@ void ARMATURE_OT_flip_names(wmOperatorType *ot)
"do_strip_numbers",
false,
"Strip Numbers",
- "Try to remove right-most dot-number from flipped names "
- "(WARNING: may result in incoherent naming in some cases)");
+ "Try to remove right-most dot-number from flipped names.\n"
+ "Warning: May result in incoherent naming in some cases");
}
/** \} */
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 226253cc063..65f30c3729f 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -961,131 +961,145 @@ bool ED_armature_edit_deselect_all_visible_multi(bContext *C)
/** \name Select Cursor Pick API
* \{ */
-/* context: editmode armature in view3d */
-bool ED_armature_edit_select_pick(
- bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+bool ED_armature_edit_select_pick_bone(bContext *C,
+ Base *basact,
+ EditBone *ebone,
+ const int selmask,
+ const bool extend,
+ const bool deselect,
+ const bool toggle)
{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewContext vc;
- EditBone *nearBone = NULL;
- int selmask;
- Base *basact = NULL;
+ if (!ebone) {
+ return false;
+ }
- ED_view3d_viewcontext_init(C, &vc, depsgraph);
- vc.mval[0] = mval[0];
- vc.mval[1] = mval[1];
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ View3D *v3d = CTX_wm_view3d(C);
- nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask);
- if (nearBone) {
- ED_view3d_viewcontext_init_object(&vc, basact->object);
- bArmature *arm = vc.obedit->data;
+ BLI_assert(BKE_object_is_in_editmode(basact->object));
+ bArmature *arm = basact->object->data;
- if (!EBONE_SELECTABLE(arm, nearBone)) {
- return false;
- }
+ if (!EBONE_SELECTABLE(arm, ebone)) {
+ return false;
+ }
- if (!extend && !deselect && !toggle) {
- uint bases_len = 0;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &bases_len);
- ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
- MEM_freeN(bases);
- }
+ if (!extend && !deselect && !toggle) {
+ uint bases_len = 0;
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
+ view_layer, v3d, &bases_len);
+ ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
+ MEM_freeN(bases);
+ }
- /* by definition the non-root connected bones have no root point drawn,
- * so a root selection needs to be delivered to the parent tip */
+ /* By definition the non-root connected bones have no root point drawn,
+ * so a root selection needs to be delivered to the parent tip. */
- if (selmask & BONE_SELECTED) {
- if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
- /* click in a chain */
- if (extend) {
- /* select this bone */
- nearBone->flag |= BONE_TIPSEL;
- nearBone->parent->flag |= BONE_TIPSEL;
- }
- else if (deselect) {
- /* deselect this bone */
- nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
- /* only deselect parent tip if it is not selected */
- if (!(nearBone->parent->flag & BONE_SELECTED)) {
- nearBone->parent->flag &= ~BONE_TIPSEL;
- }
+ if (selmask & BONE_SELECTED) {
+ if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
+ /* Bone is in a chain. */
+ if (extend) {
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
+ }
+ else if (deselect) {
+ /* Deselect this bone. */
+ ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
+ /* Only deselect parent tip if it is not selected. */
+ if (!(ebone->parent->flag & BONE_SELECTED)) {
+ ebone->parent->flag &= ~BONE_TIPSEL;
}
- else if (toggle) {
- /* hold shift inverts this bone's selection */
- if (nearBone->flag & BONE_SELECTED) {
- /* deselect this bone */
- nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
- /* only deselect parent tip if it is not selected */
- if (!(nearBone->parent->flag & BONE_SELECTED)) {
- nearBone->parent->flag &= ~BONE_TIPSEL;
- }
- }
- else {
- /* select this bone */
- nearBone->flag |= BONE_TIPSEL;
- nearBone->parent->flag |= BONE_TIPSEL;
+ }
+ else if (toggle) {
+ /* Toggle inverts this bone's selection. */
+ if (ebone->flag & BONE_SELECTED) {
+ /* Deselect this bone. */
+ ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
+ /* Only deselect parent tip if it is not selected. */
+ if (!(ebone->parent->flag & BONE_SELECTED)) {
+ ebone->parent->flag &= ~BONE_TIPSEL;
}
}
else {
- /* select this bone */
- nearBone->flag |= BONE_TIPSEL;
- nearBone->parent->flag |= BONE_TIPSEL;
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
}
}
else {
- if (extend) {
- nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
- }
- else if (deselect) {
- nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
- }
- else if (toggle) {
- /* hold shift inverts this bone's selection */
- if (nearBone->flag & BONE_SELECTED) {
- nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
- }
- else {
- nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
- }
- }
- else {
- nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
- }
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
}
}
else {
if (extend) {
- nearBone->flag |= selmask;
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
else if (deselect) {
- nearBone->flag &= ~selmask;
+ ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
}
- else if (toggle && (nearBone->flag & selmask)) {
- nearBone->flag &= ~selmask;
+ else if (toggle) {
+ /* Toggle inverts this bone's selection. */
+ if (ebone->flag & BONE_SELECTED) {
+ ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ else {
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
+ }
}
else {
- nearBone->flag |= selmask;
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
}
-
- ED_armature_edit_sync_selection(arm->edbo);
-
- /* then now check for active status */
- if (ED_armature_ebone_selectflag_get(nearBone)) {
- arm->act_edbone = nearBone;
+ }
+ else {
+ if (extend) {
+ ebone->flag |= selmask;
}
-
- if (vc.view_layer->basact != basact) {
- ED_object_base_activate(C, basact);
+ else if (deselect) {
+ ebone->flag &= ~selmask;
+ }
+ else if (toggle && (ebone->flag & selmask)) {
+ ebone->flag &= ~selmask;
}
+ else {
+ ebone->flag |= selmask;
+ }
+ }
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
- DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
- return true;
+ ED_armature_edit_sync_selection(arm->edbo);
+
+ /* Then now check for active status. */
+ if (ED_armature_ebone_selectflag_get(ebone)) {
+ arm->act_edbone = ebone;
+ }
+
+ if (view_layer->basact != basact) {
+ ED_object_base_activate(C, basact);
}
- return false;
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
+ DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
+ return true;
+}
+
+/* context: editmode armature in view3d */
+bool ED_armature_edit_select_pick(
+ bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewContext vc;
+ EditBone *nearBone = NULL;
+ int selmask;
+ Base *basact = NULL;
+
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+ vc.mval[0] = mval[0];
+ vc.mval[1] = mval[1];
+
+ nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask);
+ return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, extend, deselect, toggle);
}
/** \} */
@@ -2127,7 +2141,7 @@ static int armature_select_mirror_exec(bContext *C, wmOperator *op)
void ARMATURE_OT_select_mirror(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Flip Active/Selected Bone";
+ ot->name = "Select Mirror";
ot->idname = "ARMATURE_OT_select_mirror";
ot->description = "Mirror the bone selection";
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index e65871c0896..f5daa427149 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -593,8 +593,8 @@ void POSE_OT_flip_names(wmOperatorType *ot)
"do_strip_numbers",
false,
"Strip Numbers",
- "Try to remove right-most dot-number from flipped names "
- "(WARNING: may result in incoherent naming in some cases)");
+ "Try to remove right-most dot-number from flipped names.\n"
+ "Warning: May result in incoherent naming in some cases");
}
/* ------------------ */
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index dd90f9f2cc3..21fcbf8886b 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -1108,17 +1108,6 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData
if (autokey) {
/* Add data-source override for the PoseChannel, to be used later. */
ANIM_relative_keyingset_add_source(&dsources, &pld->ob->id, &RNA_PoseBone, pchan);
-
- /* clear any unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
- }
- else {
- /* add unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag |= BONE_UNKEYED;
- }
}
}
}
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index a3f97000509..8fc06a5f962 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -138,6 +138,106 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select)
}
}
+void ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
+ View3D *v3d,
+ Object *ob,
+ Bone *bone,
+ const bool extend,
+ const bool deselect,
+ const bool toggle)
+{
+ if (!ob || !ob->pose) {
+ return;
+ }
+
+ Object *ob_act = OBACT(view_layer);
+ BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
+
+ /* If the bone cannot be affected, don't do anything. */
+ if (bone == NULL || (bone->flag & BONE_UNSELECTABLE)) {
+ return;
+ }
+ bArmature *arm = ob->data;
+
+ /* Since we do unified select, we don't shift+select a bone if the
+ * armature object was not active yet.
+ * Note, special exception for armature mode so we can do multi-select
+ * we could check for multi-select explicitly but think its fine to
+ * always give predictable behavior in weight paint mode - campbell */
+ if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
+ /* When we are entering into posemode via toggle-select,
+ * from another active object - always select the bone. */
+ if (!extend && !deselect && toggle) {
+ /* Re-select the bone again later in this function. */
+ bone->flag &= ~BONE_SELECTED;
+ }
+ }
+
+ if (!extend && !deselect && !toggle) {
+ {
+ /* Don't use 'BKE_object_pose_base_array_get_unique'
+ * because we may be selecting from object mode. */
+ FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
+ Object *ob_iter = base_iter->object;
+ if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
+ if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
+ ED_pose_bone_select_tag_update(ob_iter);
+ }
+ }
+ }
+ FOREACH_VISIBLE_BASE_END;
+ }
+ bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_bone = bone;
+ }
+ else {
+ if (extend) {
+ bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_bone = bone;
+ }
+ else if (deselect) {
+ bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ else if (toggle) {
+ if (bone->flag & BONE_SELECTED) {
+ /* If not active, we make it active. */
+ if (bone != arm->act_bone) {
+ arm->act_bone = bone;
+ }
+ else {
+ bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ }
+ else {
+ bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_bone = bone;
+ }
+ }
+ }
+
+ if (ob_act) {
+ /* In weightpaint we select the associated vertex group too. */
+ if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
+ if (bone == arm->act_bone) {
+ ED_vgroup_select_by_name(ob_act, bone->name);
+ DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY);
+ }
+ }
+ /* If there are some dependencies for visualizing armature state
+ * (e.g. Mask Modifier in 'Armature' mode), force update.
+ */
+ else if (arm->flag & ARM_HAS_VIZ_DEPS) {
+ /* NOTE: ob not ob_act here is intentional - it's the source of the
+ * bones being selected [T37247]
+ */
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+
+ /* Tag armature for copy-on-write update (since act_bone is in armature not object). */
+ DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
+ }
+}
+
/**
* Called for mode-less pose selection.
* assumes the active object is still on old situation.
@@ -159,96 +259,12 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
return 0;
}
- Object *ob_act = OBACT(view_layer);
- BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
-
/* Callers happen to already get the active base */
Base *base_dummy = NULL;
nearBone = ED_armature_pick_bone_from_selectbuffer(
&base, 1, buffer, hits, 1, do_nearest, &base_dummy);
- /* if the bone cannot be affected, don't do anything */
- if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) {
- bArmature *arm = ob->data;
-
- /* since we do unified select, we don't shift+select a bone if the
- * armature object was not active yet.
- * note, special exception for armature mode so we can do multi-select
- * we could check for multi-select explicitly but think its fine to
- * always give predictable behavior in weight paint mode - campbell */
- if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
- /* when we are entering into posemode via toggle-select,
- * from another active object - always select the bone. */
- if (!extend && !deselect && toggle) {
- /* re-select below */
- nearBone->flag &= ~BONE_SELECTED;
- }
- }
-
- if (!extend && !deselect && !toggle) {
- {
- /* Don't use 'BKE_object_pose_base_array_get_unique'
- * because we may be selecting from object mode. */
- FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
- Object *ob_iter = base_iter->object;
- if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
- if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
- ED_pose_bone_select_tag_update(ob_iter);
- }
- }
- }
- FOREACH_VISIBLE_BASE_END;
- }
- nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_bone = nearBone;
- }
- else {
- if (extend) {
- nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_bone = nearBone;
- }
- else if (deselect) {
- nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
- else if (toggle) {
- if (nearBone->flag & BONE_SELECTED) {
- /* if not active, we make it active */
- if (nearBone != arm->act_bone) {
- arm->act_bone = nearBone;
- }
- else {
- nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
- }
- else {
- nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_bone = nearBone;
- }
- }
- }
-
- if (ob_act) {
- /* in weightpaint we select the associated vertex group too */
- if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
- if (nearBone == arm->act_bone) {
- ED_vgroup_select_by_name(ob_act, nearBone->name);
- DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY);
- }
- }
- /* if there are some dependencies for visualizing armature state
- * (e.g. Mask Modifier in 'Armature' mode), force update
- */
- else if (arm->flag & ARM_HAS_VIZ_DEPS) {
- /* NOTE: ob not ob_act here is intentional - it's the source of the
- * bones being selected [T37247]
- */
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- }
-
- /* tag armature for copy-on-write update (since act_bone is in armature not object) */
- DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
- }
- }
+ ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, extend, deselect, toggle);
return nearBone != NULL;
}
@@ -1268,7 +1284,7 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op)
void POSE_OT_select_mirror(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Flip Active/Selected Bone";
+ ot->name = "Select Mirror";
ot->idname = "POSE_OT_select_mirror";
ot->description = "Mirror the bone selection";
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index 93d36abe792..d32faf9a9ea 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -19,6 +19,28 @@
/** \file
* \ingroup edarmature
+ *
+ * Pose 'Sliding' Tools
+ * ====================
+ *
+ * - Push & Relax, Breakdowner
+
+ * These tools provide the animator with various capabilities
+ * for interactively controlling the spacing of poses, but also
+ * for 'pushing' and/or 'relaxing' extremes as they see fit.
+ *
+ * - Propagate
+
+ * This tool copies elements of the selected pose to successive
+ * keyframes, allowing the animator to go back and modify the poses
+ * for some "static" pose controls, without having to repeatedly
+ * doing a "next paste" dance.
+ *
+ * - Pose Sculpting (TODO)
+
+ * This is yet to be implemented, but the idea here is to use
+ * sculpting techniques to make it easier to pose rigs by allowing
+ * rigs to be manipulated using a familiar paint-based interface.
*/
#include "MEM_guardedalloc.h"
@@ -33,6 +55,7 @@
#include "DNA_armature_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_vec_types.h"
#include "BKE_fcurve.h"
#include "BKE_nla.h"
@@ -41,6 +64,7 @@
#include "BKE_layer.h"
#include "BKE_object.h"
#include "BKE_report.h"
+#include "BKE_screen.h"
#include "BKE_unit.h"
#include "RNA_access.h"
@@ -50,45 +74,71 @@
#include "WM_types.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "ED_armature.h"
#include "ED_keyframes_draw.h"
#include "ED_markers.h"
#include "ED_numinput.h"
#include "ED_screen.h"
+#include "ED_space_api.h"
+
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_matrix.h"
+#include "GPU_state.h"
#include "armature_intern.h"
-/* **************************************************** */
-/* == POSE 'SLIDING' TOOLS ==
- *
- * A) Push & Relax, Breakdowner
- * These tools provide the animator with various capabilities
- * for interactively controlling the spacing of poses, but also
- * for 'pushing' and/or 'relaxing' extremes as they see fit.
- *
- * B) Propagate
- * This tool copies elements of the selected pose to successive
- * keyframes, allowing the animator to go back and modify the poses
- * for some "static" pose controls, without having to repeatedly
- * doing a "next paste" dance.
- *
- * C) Pose Sculpting
- * This is yet to be implemented, but the idea here is to use
- * sculpting techniques to make it easier to pose rigs by allowing
- * rigs to be manipulated using a familiar paint-based interface.
- */
+#include "BLF_api.h"
+
+/* Pixel distance from 0% to 100%. */
+#define SLIDE_PIXEL_DISTANCE (300 * U.pixelsize)
+#define OVERSHOOT_RANGE_DELTA 0.2f
+
/* **************************************************** */
/* A) Push & Relax, Breakdowner */
-/* Temporary data shared between these operators */
+/** Axis Locks. */
+typedef enum ePoseSlide_AxisLock {
+ PS_LOCK_X = (1 << 0),
+ PS_LOCK_Y = (1 << 1),
+ PS_LOCK_Z = (1 << 2),
+} ePoseSlide_AxisLock;
+
+/** Pose Sliding Modes. */
+typedef enum ePoseSlide_Modes {
+ /** Exaggerate the pose. */
+ POSESLIDE_PUSH = 0,
+ /** soften the pose. */
+ POSESLIDE_RELAX,
+ /** Slide between the endpoint poses, finding a 'soft' spot. */
+ POSESLIDE_BREAKDOWN,
+ POSESLIDE_PUSH_REST,
+ POSESLIDE_RELAX_REST,
+} ePoseSlide_Modes;
+
+/** Transforms/Channels to Affect. */
+typedef enum ePoseSlide_Channels {
+ PS_TFM_ALL = 0, /* All transforms and properties */
+
+ PS_TFM_LOC, /* Loc/Rot/Scale */
+ PS_TFM_ROT,
+ PS_TFM_SIZE,
+
+ PS_TFM_BBONE_SHAPE, /* Bendy Bones */
+
+ PS_TFM_PROPS, /* Custom Properties */
+} ePoseSlide_Channels;
+
+/** Temporary data shared between these operators. */
typedef struct tPoseSlideOp {
/** current scene */
Scene *scene;
/** area that we're operating in (needed for modal()) */
ScrArea *area;
- /** region that we're operating in (needed for modal()) */
- ARegion *region;
+ /** Header of the region used for drawing the slider. */
+ ARegion *region_header;
/** len of the PoseSlideObject array. */
uint objects_len;
@@ -105,55 +155,57 @@ typedef struct tPoseSlideOp {
/** frame after current frame (blend-to) - global time */
int nextFrame;
- /** sliding mode (ePoseSlide_Modes) */
- short mode;
+ /** Sliding Mode. */
+ ePoseSlide_Modes mode;
/** unused for now, but can later get used for storing runtime settings.... */
short flag;
- /** which transforms/channels are affected (ePoseSlide_Channels) */
- short channels;
- /** axis-limits for transforms (ePoseSlide_AxisLock) */
- short axislock;
+ /* Store overlay settings when invoking the operator. Bones will be temporarily hidden. */
+ int overlay_flag;
+
+ /** Which transforms/channels are affected. */
+ ePoseSlide_Channels channels;
+ /** Axis-limits for transforms. */
+ ePoseSlide_AxisLock axislock;
- /** 0-1 value for determining the influence of whatever is relevant */
- float percentage;
+ /** Allow overshoot or clamp between 0% and 100%. */
+ bool overshoot;
- /** numeric input */
+ /** Reduces factor delta from mouse movement. */
+ bool precision;
+
+ /** Move factor in 10% steps. */
+ bool increments;
+
+ /** Draw callback handler. */
+ void *draw_handle;
+
+ /** Accumulative, unclamped and unrounded factor. */
+ float raw_factor;
+
+ /** 0-1 value for determining the influence of whatever is relevant. */
+ float factor;
+
+ /** Last cursor position in screen space used for mouse movement delta calculation. */
+ int last_cursor_x;
+
+ /** Numeric input. */
NumInput num;
struct tPoseSlideObject *ob_data_array;
} tPoseSlideOp;
typedef struct tPoseSlideObject {
- Object *ob; /* active object that Pose Info comes from */
- float prevFrameF; /* prevFrame, but in local action time (for F-Curve lookups to work) */
- float nextFrameF; /* nextFrame, but in local action time (for F-Curve lookups to work) */
+ /** Active object that Pose Info comes from. */
+ Object *ob;
+ /** `prevFrame`, but in local action time (for F-Curve look-ups to work). */
+ float prevFrameF;
+ /** `nextFrame`, but in local action time (for F-Curve look-ups to work). */
+ float nextFrameF;
bool valid;
} tPoseSlideObject;
-/* Pose Sliding Modes */
-typedef enum ePoseSlide_Modes {
- POSESLIDE_PUSH = 0, /* exaggerate the pose... */
- POSESLIDE_RELAX, /* soften the pose... */
- POSESLIDE_BREAKDOWN, /* slide between the endpoint poses, finding a 'soft' spot */
- POSESLIDE_PUSH_REST,
- POSESLIDE_RELAX_REST,
-} ePoseSlide_Modes;
-
-/* Transforms/Channels to Affect */
-typedef enum ePoseSlide_Channels {
- PS_TFM_ALL = 0, /* All transforms and properties */
-
- PS_TFM_LOC, /* Loc/Rot/Scale */
- PS_TFM_ROT,
- PS_TFM_SIZE,
-
- PS_TFM_BBONE_SHAPE, /* Bendy Bones */
-
- PS_TFM_PROPS, /* Custom Properties */
-} ePoseSlide_Channels;
-
-/* Property enum for ePoseSlide_Channels */
+/** Property enum for #ePoseSlide_Channels. */
static const EnumPropertyItem prop_channels_types[] = {
{PS_TFM_ALL,
"ALL",
@@ -168,13 +220,6 @@ static const EnumPropertyItem prop_channels_types[] = {
{0, NULL, 0, NULL, NULL},
};
-/* Axis Locks */
-typedef enum ePoseSlide_AxisLock {
- PS_LOCK_X = (1 << 0),
- PS_LOCK_Y = (1 << 1),
- PS_LOCK_Z = (1 << 2),
-} ePoseSlide_AxisLock;
-
/* Property enum for ePoseSlide_AxisLock */
static const EnumPropertyItem prop_axis_lock_types[] = {
{0, "FREE", 0, "Free", "All axes are affected"},
@@ -187,33 +232,284 @@ static const EnumPropertyItem prop_axis_lock_types[] = {
/* ------------------------------------ */
-/* operator init */
+static void draw_overshoot_triangle(const uint8_t color[4],
+ const bool facing_right,
+ const float x,
+ const float y)
+{
+ const uint shdr_pos_2d = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_polygon_smooth(true);
+ immUniformColor3ubvAlpha(color, 225);
+ const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize;
+ const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize;
+
+ immBegin(GPU_PRIM_TRIS, 3);
+ immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y);
+ immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2);
+ immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2);
+ immEnd();
+
+ GPU_polygon_smooth(false);
+ GPU_blend(GPU_BLEND_NONE);
+ immUnbindProgram();
+}
+
+static void draw_ticks(const float start_factor,
+ const float end_factor,
+ const float line_start[2],
+ const float base_tick_height,
+ const float line_width,
+ const uint8_t color_overshoot[4],
+ const uint8_t color_line[4])
+{
+ /* Use factor represented as 0-100 int to avoid floating point precision problems. */
+ const int tick_increment = 10;
+
+ /* Round initial_tick_factor up to the next tick_increment. */
+ int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment;
+
+ while (tick_percentage <= (int)(end_factor * 100)) {
+ float tick_height;
+ /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit
+ * smaller and the rest is the minimum size. */
+ if (tick_percentage % 100 == 0) {
+ tick_height = base_tick_height;
+ }
+ else if (tick_percentage % 50 == 0) {
+ tick_height = base_tick_height * 0.8;
+ }
+ else {
+ tick_height = base_tick_height * 0.5;
+ }
+
+ const float x = line_start[0] +
+ (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE;
+ const rctf tick_rect = {
+ .xmin = x - (line_width / 2),
+ .xmax = x + (line_width / 2),
+ .ymin = line_start[1] - (tick_height / 2),
+ .ymax = line_start[1] + (tick_height / 2),
+ };
+
+ if (tick_percentage < 0 || tick_percentage > 100) {
+ UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255);
+ }
+ else {
+ UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255);
+ }
+ tick_percentage += tick_increment;
+ }
+}
+
+static void draw_main_line(const rctf *main_line_rect,
+ const float factor,
+ const bool overshoot,
+ const uint8_t color_overshoot[4],
+ const uint8_t color_line[4])
+{
+ if (overshoot) {
+ /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */
+ const float line_zero_percent = main_line_rect->xmin -
+ ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) *
+ SLIDE_PIXEL_DISTANCE);
+
+ const float clamped_line_zero_percent = clamp_f(
+ line_zero_percent, main_line_rect->xmin, main_line_rect->xmax);
+ const float clamped_line_hundred_percent = clamp_f(
+ line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax);
+
+ const rctf left_overshoot_line_rect = {
+ .xmin = main_line_rect->xmin,
+ .xmax = clamped_line_zero_percent,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ const rctf right_overshoot_line_rect = {
+ .xmin = clamped_line_hundred_percent,
+ .xmax = main_line_rect->xmax,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255);
+ UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255);
+
+ const rctf non_overshoot_line_rect = {
+ .xmin = clamped_line_zero_percent,
+ .xmax = clamped_line_hundred_percent,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255);
+ }
+ else {
+ UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255);
+ }
+}
+
+static void draw_backdrop(const int fontid,
+ const rctf *main_line_rect,
+ const float color_bg[4],
+ const short region_y_size,
+ const float base_tick_height)
+{
+ float string_pixel_size[2];
+ const char *percentage_string_placeholder = "000%%";
+ BLF_width_and_height(fontid,
+ percentage_string_placeholder,
+ sizeof(percentage_string_placeholder),
+ &string_pixel_size[0],
+ &string_pixel_size[1]);
+ const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize};
+ const rctf backdrop_rect = {
+ .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0],
+ .xmax = main_line_rect->xmax + pad[0],
+ .ymin = pad[1],
+ .ymax = region_y_size - pad[1],
+ };
+ UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg);
+}
+
+/**
+ * Draw an on screen Slider for a Pose Slide Operator.
+ */
+static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg)
+{
+ tPoseSlideOp *pso = arg;
+
+ /* Only draw in region from which the Operator was started. */
+ if (region != pso->region_header) {
+ return;
+ }
+
+ uint8_t color_text[4];
+ uint8_t color_line[4];
+ uint8_t color_handle[4];
+ uint8_t color_overshoot[4];
+ float color_bg[4];
+
+ /* Get theme colors. */
+ UI_GetThemeColor4ubv(TH_TEXT, color_text);
+ UI_GetThemeColor4ubv(TH_TEXT, color_line);
+ UI_GetThemeColor4ubv(TH_TEXT, color_overshoot);
+ UI_GetThemeColor4ubv(TH_ACTIVE, color_handle);
+ UI_GetThemeColor3fv(TH_BACK, color_bg);
+
+ color_bg[3] = 0.5f;
+ color_overshoot[0] = color_overshoot[0] * 0.7;
+ color_overshoot[1] = color_overshoot[1] * 0.7;
+ color_overshoot[2] = color_overshoot[2] * 0.7;
+
+ /* Get the default font. */
+ const uiStyle *style = UI_style_get();
+ const uiFontStyle *fstyle = &style->widget;
+ const int fontid = fstyle->uifont_id;
+ BLF_color3ubv(fontid, color_text);
+ BLF_rotation(fontid, 0.0f);
+
+ const float line_width = 1.5 * U.pixelsize;
+ const float base_tick_height = 12.0 * U.pixelsize;
+ const float line_y = region->winy / 2;
+
+ rctf main_line_rect = {
+ .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2),
+ .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2),
+ .ymin = line_y - line_width / 2,
+ .ymax = line_y + line_width / 2,
+ };
+ float line_start_factor = 0;
+ int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->factor;
+
+ if (pso->overshoot) {
+ main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
+ main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
+ line_start_factor = pso->factor - 0.5f - OVERSHOOT_RANGE_DELTA;
+ handle_pos_x = region->winx / 2;
+ }
+
+ draw_backdrop(fontid, &main_line_rect, color_bg, pso->region_header->winy, base_tick_height);
+
+ draw_main_line(&main_line_rect, pso->factor, pso->overshoot, color_overshoot, color_line);
+
+ const float factor_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1;
+ const float line_start_position[2] = {main_line_rect.xmin, line_y};
+ draw_ticks(line_start_factor,
+ line_start_factor + factor_range,
+ line_start_position,
+ base_tick_height,
+ line_width,
+ color_overshoot,
+ color_line);
+
+ /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100%
+ * range.*/
+ if (pso->overshoot) {
+ if (pso->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) {
+ draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y);
+ }
+ if (pso->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) {
+ draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y);
+ }
+ }
+
+ char percentage_string[256];
+
+ /* Draw handle indicating current factor. */
+ const rctf handle_rect = {
+ .xmin = handle_pos_x - (line_width),
+ .xmax = handle_pos_x + (line_width),
+ .ymin = line_y - (base_tick_height / 2),
+ .ymax = line_y + (base_tick_height / 2),
+ };
+
+ UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255);
+ BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->factor * 100);
+
+ /* Draw percentage string. */
+ float percentage_string_pixel_size[2];
+ BLF_width_and_height(fontid,
+ percentage_string,
+ sizeof(percentage_string),
+ &percentage_string_pixel_size[0],
+ &percentage_string_pixel_size[1]);
+
+ BLF_position(fontid,
+ main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2,
+ (region->winy / 2) - percentage_string_pixel_size[1] / 2,
+ 0.0f);
+ BLF_draw(fontid, percentage_string, sizeof(percentage_string));
+}
+
+/** Operator custom-data initialization. */
static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
{
tPoseSlideOp *pso;
- /* init slide-op data */
+ /* Init slide-op data. */
pso = op->customdata = MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
- /* get info from context */
+ /* Get info from context. */
pso->scene = CTX_data_scene(C);
- pso->area = CTX_wm_area(C); /* only really needed when doing modal() */
- pso->region = CTX_wm_region(C); /* only really needed when doing modal() */
+ pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */
+ pso->region_header = CTX_wm_region(C); /* Only really needed when doing modal(). */
pso->cframe = pso->scene->r.cfra;
pso->mode = mode;
- /* set range info from property values - these may get overridden for the invoke() */
- pso->percentage = RNA_float_get(op->ptr, "percentage");
+ /* Set range info from property values - these may get overridden for the invoke(). */
+ pso->factor = RNA_float_get(op->ptr, "factor");
+ pso->raw_factor = pso->factor;
pso->prevFrame = RNA_int_get(op->ptr, "prev_frame");
pso->nextFrame = RNA_int_get(op->ptr, "next_frame");
- /* get the set of properties/axes that can be operated on */
+ /* Get the set of properties/axes that can be operated on. */
pso->channels = RNA_enum_get(op->ptr, "channels");
pso->axislock = RNA_enum_get(op->ptr, "axis_lock");
- /* for each Pose-Channel which gets affected, get the F-Curves for that channel
- * and set the relevant transform flags... */
+ /* For each Pose-Channel which gets affected, get the F-Curves for that channel
+ * and set the relevant transform flags. */
poseAnim_mapping_get(C, &pso->pfLinks);
Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
@@ -233,65 +529,80 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
ob_data->ob = ob_iter;
ob_data->valid = true;
- /* apply NLA mapping corrections so the frame lookups work */
+ /* Apply NLA mapping corrections so the frame look-ups work. */
ob_data->prevFrameF = BKE_nla_tweakedit_remap(
ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
ob_data->nextFrameF = BKE_nla_tweakedit_remap(
ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP);
- /* set depsgraph flags */
- /* make sure the lock is set OK, unlock can be accidentally saved? */
+ /* Set depsgraph flags. */
+ /* Make sure the lock is set OK, unlock can be accidentally saved? */
ob_data->ob->pose->flag |= POSE_LOCKED;
ob_data->ob->pose->flag &= ~POSE_DO_UNLOCK;
}
MEM_freeN(objects);
- /* do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up
- * to the caller of this (usually only invoke() will do it, to make things more efficient).
- */
+ /* Do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up
+ * to the caller of this (usually only invoke() will do it, to make things more efficient). */
BLI_dlrbTree_init(&pso->keys);
/* Initialize numeric input. */
initNumInput(&pso->num);
- pso->num.idx_max = 0; /* one axis */
+ pso->num.idx_max = 0; /* One axis. */
pso->num.val_flag[0] |= NUM_NO_NEGATIVE;
- pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */
+ pso->num.unit_type[0] = B_UNIT_NONE; /* Percentages don't have any units. */
+
+ /* Register UI drawing callback. */
+ ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER);
+ if (region_header != NULL) {
+ pso->region_header = region_header;
+ pso->draw_handle = ED_region_draw_cb_activate(
+ region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL);
+ }
- /* return status is whether we've got all the data we were requested to get */
+ /* Return status is whether we've got all the data we were requested to get. */
return 1;
}
-/* exiting the operator - free data */
+/**
+ * Exiting the operator (free data).
+ */
static void pose_slide_exit(wmOperator *op)
{
tPoseSlideOp *pso = op->customdata;
- /* if data exists, clear its data and exit */
- if (pso) {
- /* free the temp pchan links and their data */
- poseAnim_mapping_free(&pso->pfLinks);
+ /* Hide Bone Overlay. */
+ View3D *v3d = pso->area->spacedata.first;
+ v3d->overlay.flag = pso->overlay_flag;
- /* free RB-BST for keyframes (if it contained data) */
- BLI_dlrbTree_free(&pso->keys);
+ /* Remove UI drawing callback. */
+ ED_region_draw_cb_exit(pso->region_header->type, pso->draw_handle);
- if (pso->ob_data_array != NULL) {
- MEM_freeN(pso->ob_data_array);
- }
+ /* Free the temp pchan links and their data. */
+ poseAnim_mapping_free(&pso->pfLinks);
+
+ /* Free RB-BST for keyframes (if it contained data). */
+ BLI_dlrbTree_free(&pso->keys);
- /* free data itself */
- MEM_freeN(pso);
+ if (pso->ob_data_array != NULL) {
+ MEM_freeN(pso->ob_data_array);
}
- /* cleanup */
+ /* Free data itself. */
+ MEM_freeN(pso);
+
+ /* Cleanup. */
op->customdata = NULL;
}
/* ------------------------------------ */
-/* helper for apply() / reset() - refresh the data */
+/**
+ * Helper for apply() / reset() - refresh the data.
+ */
static void pose_slide_refresh(bContext *C, tPoseSlideOp *pso)
{
- /* wrapper around the generic version, allowing us to add some custom stuff later still */
+ /* Wrapper around the generic version, allowing us to add some custom stuff later still. */
for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) {
tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index];
if (ob_data->valid) {
@@ -323,7 +634,9 @@ static bool pose_frame_range_from_object_get(tPoseSlideOp *pso,
return false;
}
-/* helper for apply() - perform sliding for some value */
+/**
+ * Helper for apply() - perform sliding for some value.
+ */
static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, float *val)
{
float prevFrameF, nextFrameF;
@@ -333,17 +646,17 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo
pose_frame_range_from_object_get(pso, ob, &prevFrameF, &nextFrameF);
- /* get keyframe values for endpoint poses to blend with */
- /* previous/start */
+ /* Get keyframe values for endpoint poses to blend with. */
+ /* Previous/start. */
sVal = evaluate_fcurve(fcu, prevFrameF);
- /* next/end */
+ /* Next/end. */
eVal = evaluate_fcurve(fcu, nextFrameF);
- /* calculate the relative weights of the endpoints */
+ /* Calculate the relative weights of the endpoints. */
if (pso->mode == POSESLIDE_BREAKDOWN) {
- /* get weights from the percentage control */
- w1 = pso->percentage; /* this must come second */
- w2 = 1.0f - w1; /* this must come first */
+ /* Get weights from the factor control. */
+ w1 = pso->factor; /* This must come second. */
+ w2 = 1.0f - w1; /* This must come first. */
}
else {
/* - these weights are derived from the relative distance of these
@@ -366,30 +679,37 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo
* the value the current frame is closer to.
*/
switch (pso->mode) {
- case POSESLIDE_PUSH: /* make the current pose more pronounced */
+ case POSESLIDE_PUSH: /* Make the current pose more pronounced. */
{
/* Slide the pose away from the breakdown pose in the timeline */
- (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->percentage;
+ (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor;
break;
}
- case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
+ case POSESLIDE_RELAX: /* Make the current pose more like its surrounding ones. */
{
/* Slide the pose towards the breakdown pose in the timeline */
- (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->percentage;
+ (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor;
break;
}
- case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
+ case POSESLIDE_BREAKDOWN: /* Make the current pose slide around between the endpoints. */
{
/* Perform simple linear interpolation -
- * coefficient for start must come from pso->percentage. */
+ * coefficient for start must come from pso->factor. */
/* TODO: make this use some kind of spline interpolation instead? */
(*val) = ((sVal * w2) + (eVal * w1));
break;
}
+ /* Those are handled in pose_slide_rest_pose_apply. */
+ case POSESLIDE_PUSH_REST:
+ case POSESLIDE_RELAX_REST: {
+ break;
+ }
}
}
-/* helper for apply() - perform sliding for some 3-element vector */
+/**
+ * Helper for apply() - perform sliding for some 3-element vector.
+ */
static void pose_slide_apply_vec3(tPoseSlideOp *pso,
tPChanFCurveLink *pfl,
float vec[3],
@@ -398,30 +718,32 @@ static void pose_slide_apply_vec3(tPoseSlideOp *pso,
LinkData *ld = NULL;
char *path = NULL;
- /* get the path to use... */
+ /* Get the path to use. */
path = BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
- /* using this path, find each matching F-Curve for the variables we're interested in */
+ /* Using this path, find each matching F-Curve for the variables we're interested in. */
while ((ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path))) {
FCurve *fcu = (FCurve *)ld->data;
const int idx = fcu->array_index;
const int lock = pso->axislock;
- /* check if this F-Curve is ok given the current axis locks */
+ /* Check if this F-Curve is ok given the current axis locks. */
BLI_assert(fcu->array_index < 3);
if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) ||
((lock & PS_LOCK_Z) && (idx == 2))) {
- /* just work on these channels one by one... there's no interaction between values */
+ /* Just work on these channels one by one... there's no interaction between values. */
pose_slide_apply_val(pso, fcu, pfl->ob, &vec[fcu->array_index]);
}
}
- /* free the temp path we got */
+ /* Free the temp path we got. */
MEM_freeN(path);
}
-/* helper for apply() - perform sliding for custom properties or bbone properties */
+/**
+ * Helper for apply() - perform sliding for custom properties or bbone properties.
+ */
static void pose_slide_apply_props(tPoseSlideOp *pso,
tPChanFCurveLink *pfl,
const char prop_prefix[])
@@ -430,7 +752,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
LinkData *ld;
int len = strlen(pfl->pchan_path);
- /* setup pointer RNA for resolving paths */
+ /* Setup pointer RNA for resolving paths. */
RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr);
/* - custom properties are just denoted using ["..."][etc.] after the end of the base path,
@@ -446,22 +768,21 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
continue;
}
- /* do we have a match?
- * - bPtr is the RNA Path with the standard part chopped off
- * - pPtr is the chunk of the path which is left over
+ /* Do we have a match?
+ * - bPtr is the RNA Path with the standard part chopped off.
+ * - pPtr is the chunk of the path which is left over.
*/
bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len;
pPtr = strstr(bPtr, prop_prefix);
if (pPtr) {
- /* use RNA to try and get a handle on this property, then, assuming that it is just
- * numerical, try and grab the value as a float for temp editing before setting back
- */
+ /* Use RNA to try and get a handle on this property, then, assuming that it is just
+ * numerical, try and grab the value as a float for temp editing before setting back. */
PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr);
if (prop) {
switch (RNA_property_type(prop)) {
- /* continuous values that can be smoothly interpolated... */
+ /* Continuous values that can be smoothly interpolated. */
case PROP_FLOAT: {
float tval = RNA_property_float_get(&ptr, prop);
pose_slide_apply_val(pso, fcu, pfl->ob, &tval);
@@ -475,7 +796,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
break;
}
- /* values which can only take discrete values */
+ /* Values which can only take discrete values. */
case PROP_BOOLEAN: {
float tval = (float)RNA_property_boolean_get(&ptr, prop);
pose_slide_apply_val(pso, fcu, pfl->ob, &tval);
@@ -484,14 +805,13 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
break;
}
case PROP_ENUM: {
- /* don't handle this case - these don't usually represent interchangeable
- * set of values which should be interpolated between
- */
+ /* Don't handle this case - these don't usually represent interchangeable
+ * set of values which should be interpolated between. */
break;
}
default:
- /* cannot handle */
+ /* Cannot handle. */
// printf("Cannot Pose Slide non-numerical property\n");
break;
}
@@ -500,7 +820,9 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
}
}
-/* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
+/**
+ * Helper for apply() - perform sliding for quaternion rotations (using quat blending).
+ */
static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
{
FCurve *fcu_w = NULL, *fcu_x = NULL, *fcu_y = NULL, *fcu_z = NULL;
@@ -515,17 +837,17 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
return;
}
- /* get the path to use - this should be quaternion rotations only (needs care) */
+ /* Get the path to use - this should be quaternion rotations only (needs care). */
path = BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
- /* get the current frame number */
+ /* Get the current frame number. */
cframe = (float)pso->cframe;
- /* using this path, find each matching F-Curve for the variables we're interested in */
+ /* Using this path, find each matching F-Curve for the variables we're interested in. */
while ((ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path))) {
FCurve *fcu = (FCurve *)ld->data;
- /* assign this F-Curve to one of the relevant pointers... */
+ /* Assign this F-Curve to one of the relevant pointers. */
switch (fcu->array_index) {
case 3: /* z */
fcu_z = fcu;
@@ -542,14 +864,14 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
}
}
- /* only if all channels exist, proceed */
+ /* Only if all channels exist, proceed. */
if (fcu_w && fcu_x && fcu_y && fcu_z) {
float quat_final[4];
- /* perform blending */
+ /* Perform blending. */
if (pso->mode == POSESLIDE_BREAKDOWN) {
/* Just perform the interpolation between quat_prev and
- * quat_next using pso->percentage as a guide. */
+ * quat_next using pso->factor as a guide. */
float quat_prev[4];
float quat_next[4];
@@ -566,7 +888,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
normalize_qt(quat_prev);
normalize_qt(quat_next);
- interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->percentage);
+ interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->factor);
}
else {
/* POSESLIDE_PUSH and POSESLIDE_RELAX. */
@@ -584,11 +906,11 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
normalize_qt(quat_curr);
if (pso->mode == POSESLIDE_PUSH) {
- interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->percentage);
+ interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->factor);
}
else {
BLI_assert(pso->mode == POSESLIDE_RELAX);
- interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->percentage);
+ interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->factor);
}
}
@@ -596,24 +918,24 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
quat_to_compatible_quat(pchan->quat, quat_final, pchan->quat);
}
- /* free the path now */
+ /* Free the path now. */
MEM_freeN(path);
}
static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], float default_value)
{
- /* We only slide to the rest pose. So only use the default rest pose value */
+ /* We only slide to the rest pose. So only use the default rest pose value. */
const int lock = pso->axislock;
for (int idx = 0; idx < 3; idx++) {
if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) ||
((lock & PS_LOCK_Z) && (idx == 2))) {
float diff_val = default_value - vec[idx];
if (pso->mode == POSESLIDE_RELAX_REST) {
- vec[idx] += pso->percentage * diff_val;
+ vec[idx] += pso->factor * diff_val;
}
else {
/* Push */
- vec[idx] -= pso->percentage * diff_val;
+ vec[idx] -= pso->factor * diff_val;
}
}
}
@@ -621,7 +943,7 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo
static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4], bool quat)
{
- /* We only slide to the rest pose. So only use the default rest pose value */
+ /* We only slide to the rest pose. So only use the default rest pose value. */
float default_values[] = {1.0f, 0.0f, 0.0f, 0.0f};
if (!quat) {
/* Axis Angle */
@@ -631,56 +953,58 @@ static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4]
for (int idx = 0; idx < 4; idx++) {
float diff_val = default_values[idx] - vec[idx];
if (pso->mode == POSESLIDE_RELAX_REST) {
- vec[idx] += pso->percentage * diff_val;
+ vec[idx] += pso->factor * diff_val;
}
else {
/* Push */
- vec[idx] -= pso->percentage * diff_val;
+ vec[idx] -= pso->factor * diff_val;
}
}
}
-/* apply() - perform the pose sliding between the current pose and the rest pose */
+/**
+ * apply() - perform the pose sliding between the current pose and the rest pose.
+ */
static void pose_slide_rest_pose_apply(bContext *C, tPoseSlideOp *pso)
{
tPChanFCurveLink *pfl;
- /* for each link, handle each set of transforms */
+ /* For each link, handle each set of transforms. */
for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
- /* valid transforms for each PoseChannel should have been noted already
- * - sliding the pose should be a straightforward exercise for location+rotation,
+ /* Valid transforms for each #bPoseChannel should have been noted already.
+ * - Sliding the pose should be a straightforward exercise for location+rotation,
* but rotations get more complicated since we may want to use quaternion blending
- * for quaternions instead...
+ * for quaternions instead.
*/
bPoseChannel *pchan = pfl->pchan;
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) {
- /* calculate these for the 'location' vector, and use location curves */
+ /* Calculate these for the 'location' vector, and use location curves. */
pose_slide_rest_pose_apply_vec3(pso, pchan->loc, 0.0f);
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) {
- /* calculate these for the 'scale' vector, and use scale curves */
+ /* Calculate these for the 'scale' vector, and use scale curves. */
pose_slide_rest_pose_apply_vec3(pso, pchan->size, 1.0f);
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) {
- /* everything depends on the rotation mode */
+ /* Everything depends on the rotation mode. */
if (pchan->rotmode > 0) {
- /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
+ /* Eulers - so calculate these for the 'eul' vector, and use euler_rotation curves. */
pose_slide_rest_pose_apply_vec3(pso, pchan->eul, 0.0f);
}
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
pose_slide_rest_pose_apply_other_rot(pso, pchan->quat, false);
}
else {
- /* quaternions - use quaternion blending */
+ /* Quaternions - use quaternion blending. */
pose_slide_rest_pose_apply_other_rot(pso, pchan->quat, true);
}
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) {
- /* bbone properties - they all start a "bbone_" prefix */
+ /* Bbone properties - they all start a "bbone_" prefix. */
/* TODO Not implemented */
// pose_slide_apply_props(pso, pfl, "bbone_");
}
@@ -693,18 +1017,20 @@ static void pose_slide_rest_pose_apply(bContext *C, tPoseSlideOp *pso)
}
}
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws. */
pose_slide_refresh(C, pso);
}
-/* apply() - perform the pose sliding based on weighting various poses */
+/**
+ * apply() - perform the pose sliding based on weighting various poses.
+ */
static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
{
tPChanFCurveLink *pfl;
/* Sanitize the frame ranges. */
if (pso->prevFrame == pso->nextFrame) {
- /* move out one step either side */
+ /* Move out one step either side. */
pso->prevFrame--;
pso->nextFrame++;
@@ -715,7 +1041,7 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
continue;
}
- /* apply NLA mapping corrections so the frame lookups work */
+ /* Apply NLA mapping corrections so the frame look-ups work. */
ob_data->prevFrameF = BKE_nla_tweakedit_remap(
ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
ob_data->nextFrameF = BKE_nla_tweakedit_remap(
@@ -723,9 +1049,9 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
}
}
- /* for each link, handle each set of transforms */
+ /* For each link, handle each set of transforms. */
for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
- /* valid transforms for each PoseChannel should have been noted already
+ /* Valid transforms for each #bPoseChannel should have been noted already
* - sliding the pose should be a straightforward exercise for location+rotation,
* but rotations get more complicated since we may want to use quaternion blending
* for quaternions instead...
@@ -733,32 +1059,32 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
bPoseChannel *pchan = pfl->pchan;
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) {
- /* calculate these for the 'location' vector, and use location curves */
+ /* Calculate these for the 'location' vector, and use location curves. */
pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) {
- /* calculate these for the 'scale' vector, and use scale curves */
+ /* Calculate these for the 'scale' vector, and use scale curves. */
pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) {
- /* everything depends on the rotation mode */
+ /* Everything depends on the rotation mode. */
if (pchan->rotmode > 0) {
- /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
+ /* Eulers - so calculate these for the 'eul' vector, and use euler_rotation curves. */
pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
}
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
/* TODO: need to figure out how to do this! */
}
else {
- /* quaternions - use quaternion blending */
+ /* Quaternions - use quaternion blending. */
pose_slide_apply_quat(pso, pfl);
}
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) {
- /* bbone properties - they all start a "bbone_" prefix */
+ /* Bbone properties - they all start a "bbone_" prefix. */
pose_slide_apply_props(pso, pfl, "bbone_");
}
@@ -769,34 +1095,45 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
}
}
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws. */
pose_slide_refresh(C, pso);
}
-/* perform auto-key-framing after changes were made + confirmed */
+/**
+ * Perform auto-key-framing after changes were made + confirmed.
+ */
static void pose_slide_autoKeyframe(bContext *C, tPoseSlideOp *pso)
{
- /* wrapper around the generic call */
+ /* Wrapper around the generic call. */
poseAnim_mapping_autoKeyframe(C, pso->scene, &pso->pfLinks, (float)pso->cframe);
}
-/* reset changes made to current pose */
+/**
+ * Reset changes made to current pose.
+ */
static void pose_slide_reset(tPoseSlideOp *pso)
{
- /* wrapper around the generic call, so that custom stuff can be added later */
+ /* Wrapper around the generic call, so that custom stuff can be added later. */
poseAnim_mapping_reset(&pso->pfLinks);
}
/* ------------------------------------ */
-/* draw percentage indicator in header */
-/* TODO: Include hints about locks here... */
-static void pose_slide_draw_status(tPoseSlideOp *pso)
+/**
+ * Draw percentage indicator in status-bar.
+ *
+ * TODO: Include hints about locks here.
+ */
+static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
{
char status_str[UI_MAX_DRAW_STR];
char limits_str[UI_MAX_DRAW_STR];
char axis_str[50];
char mode_str[32];
+ char overshoot_str[50];
+ char precision_str[50];
+ char increments_str[50];
+ char bone_vis_str[50];
switch (pso->mode) {
case POSESLIDE_PUSH:
@@ -810,25 +1147,25 @@ static void pose_slide_draw_status(tPoseSlideOp *pso)
break;
default:
- /* unknown */
+ /* Unknown. */
strcpy(mode_str, TIP_("Sliding-Tool"));
break;
}
switch (pso->axislock) {
case PS_LOCK_X:
- BLI_strncpy(axis_str, TIP_("[X]/Y/Z axis only (X to clear)"), sizeof(axis_str));
+ STRNCPY(axis_str, TIP_("[X]/Y/Z axis only (X to clear)"));
break;
case PS_LOCK_Y:
- BLI_strncpy(axis_str, TIP_("X/[Y]/Z axis only (Y to clear)"), sizeof(axis_str));
+ STRNCPY(axis_str, TIP_("X/[Y]/Z axis only (Y to clear)"));
break;
case PS_LOCK_Z:
- BLI_strncpy(axis_str, TIP_("X/Y/[Z] axis only (Z to clear)"), sizeof(axis_str));
+ STRNCPY(axis_str, TIP_("X/Y/[Z] axis only (Z to clear)"));
break;
default:
if (ELEM(pso->channels, PS_TFM_LOC, PS_TFM_ROT, PS_TFM_SIZE)) {
- BLI_strncpy(axis_str, TIP_("X/Y/Z = Axis Constraint"), sizeof(axis_str));
+ STRNCPY(axis_str, TIP_("X/Y/Z = Axis Constraint"));
}
else {
axis_str[0] = '\0';
@@ -856,93 +1193,116 @@ static void pose_slide_draw_status(tPoseSlideOp *pso)
axis_str);
break;
case PS_TFM_BBONE_SHAPE:
- BLI_strncpy(limits_str,
- TIP_("G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s"),
- sizeof(limits_str));
+ STRNCPY(limits_str, TIP_("G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s"));
break;
case PS_TFM_PROPS:
- BLI_strncpy(limits_str,
- TIP_("G/R/S/B/[C] - Custom Properties only (C to clear) | %s"),
- sizeof(limits_str));
+ STRNCPY(limits_str, TIP_("G/R/S/B/[C] - Custom Properties only (C to clear) | %s"));
break;
default:
- BLI_strncpy(
- limits_str, TIP_("G/R/S/B/C - Limit to Transform/Property Set"), sizeof(limits_str));
+ STRNCPY(limits_str, TIP_("G/R/S/B/C - Limit to Transform/Property Set"));
break;
}
+ if (pso->overshoot) {
+ STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot"));
+ }
+ else {
+ STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot"));
+ }
+
+ if (pso->precision) {
+ STRNCPY(precision_str, TIP_("[Shift] - Precision active"));
+ }
+ else {
+ STRNCPY(precision_str, TIP_("Shift - Hold for precision"));
+ }
+
+ if (pso->increments) {
+ STRNCPY(increments_str, TIP_("[Ctrl] - Increments active"));
+ }
+ else {
+ STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments"));
+ }
+
+ STRNCPY(bone_vis_str, TIP_("[H] - Toggle bone visibility"));
+
if (hasNumInput(&pso->num)) {
Scene *scene = pso->scene;
- char str_ofs[NUM_STR_REP_LEN];
+ char str_offs[NUM_STR_REP_LEN];
- outputNumInput(&pso->num, str_ofs, &scene->unit);
+ outputNumInput(&pso->num, str_offs, &scene->unit);
- BLI_snprintf(
- status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_ofs, limits_str);
+ BLI_snprintf(status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str);
}
else {
BLI_snprintf(status_str,
sizeof(status_str),
- "%s: %d %% | %s",
+ "%s: %s | %s | %s | %s | %s",
mode_str,
- (int)(pso->percentage * 100.0f),
- limits_str);
+ limits_str,
+ overshoot_str,
+ precision_str,
+ increments_str,
+ bone_vis_str);
}
- ED_area_status_text(pso->area, status_str);
+ ED_workspace_status_text(C, status_str);
+ ED_area_status_text(pso->area, "");
}
-/* common code for invoke() methods */
+/**
+ * Common code for invoke() methods.
+ */
static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
{
tPChanFCurveLink *pfl;
wmWindow *win = CTX_wm_window(C);
- /* for each link, add all its keyframes to the search tree */
+ /* For each link, add all its keyframes to the search tree. */
for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
LinkData *ld;
- /* do this for each F-Curve */
+ /* Do this for each F-Curve. */
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
FCurve *fcu = (FCurve *)ld->data;
fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0);
}
}
- /* cancel if no keyframes found... */
+ /* Cancel if no keyframes found. */
if (pso->keys.root) {
ActKeyColumn *ak;
float cframe = (float)pso->cframe;
- /* firstly, check if the current frame is a keyframe... */
+ /* Firstly, check if the current frame is a keyframe. */
ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
if (ak == NULL) {
- /* current frame is not a keyframe, so search */
+ /* Current frame is not a keyframe, so search. */
ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev(
&pso->keys, compare_ak_cfraPtr, &cframe);
ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next(
&pso->keys, compare_ak_cfraPtr, &cframe);
- /* new set the frames */
- /* prev frame */
+ /* New set the frames. */
+ /* Prev frame. */
pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1);
RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
- /* next frame */
+ /* Next frame. */
pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1);
RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
}
else {
- /* current frame itself is a keyframe, so just take keyframes on either side */
- /* prev frame */
+ /* Current frame itself is a keyframe, so just take keyframes on either side. */
+ /* Prev frame. */
pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1);
RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
- /* next frame */
+ /* Next frame. */
pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1);
RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
}
- /* apply NLA mapping corrections so the frame lookups work */
+ /* Apply NLA mapping corrections so the frame look-ups work. */
for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) {
tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index];
if (ob_data->valid) {
@@ -959,8 +1319,8 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p
return OPERATOR_CANCELLED;
}
- /* initial apply for operator... */
- /* TODO: need to calculate percentage for initial round too... */
+ /* Initial apply for operator. */
+ /* TODO: need to calculate factor for initial round too. */
if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) {
pose_slide_apply(C, pso);
}
@@ -968,32 +1328,52 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p
pose_slide_rest_pose_apply(C, pso);
}
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws. */
pose_slide_refresh(C, pso);
- /* set cursor to indicate modal */
+ /* Set cursor to indicate modal. */
WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
- /* header print */
- pose_slide_draw_status(pso);
+ /* Header print. */
+ pose_slide_draw_status(C, pso);
- /* add a modal handler for this operator */
+ /* Add a modal handler for this operator. */
WM_event_add_modal_handler(C, op);
+
+ /* Hide Bone Overlay. */
+ View3D *v3d = pso->area->spacedata.first;
+ pso->overlay_flag = v3d->overlay.flag;
+ v3d->overlay.flag |= V3D_OVERLAY_HIDE_BONES;
+
return OPERATOR_RUNNING_MODAL;
}
-/* calculate percentage based on position of mouse (we only use x-axis for now.
- * since this is more convenient for users to do), and store new percentage value
+/**
+ * Calculate factor based on mouse movement, clamp or round to increments if
+ * enabled by the user. Store the new factor value.
*/
-static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso,
- wmOperator *op,
- const wmEvent *event)
+static void pose_slide_mouse_update_factor(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event)
{
- pso->percentage = (event->x - pso->region->winrct.xmin) / ((float)pso->region->winx);
- RNA_float_set(op->ptr, "percentage", pso->percentage);
+ const float factor_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE));
+ /* Reduced factor delta in precision mode (shift held). */
+ pso->raw_factor += pso->precision ? (factor_delta / 8) : factor_delta;
+ pso->factor = pso->raw_factor;
+ pso->last_cursor_x = event->x;
+
+ if (!pso->overshoot) {
+ pso->factor = clamp_f(pso->factor, 0, 1);
+ }
+
+ if (pso->increments) {
+ pso->factor = round(pso->factor * 10) / 10;
+ }
+
+ RNA_float_set(op->ptr, "factor", pso->factor);
}
-/* handle an event to toggle channels mode */
+/**
+ * Handle an event to toggle channels mode.
+ */
static void pose_slide_toggle_channels_mode(wmOperator *op,
tPoseSlideOp *pso,
ePoseSlide_Channels channel)
@@ -1014,7 +1394,9 @@ static void pose_slide_toggle_channels_mode(wmOperator *op,
RNA_enum_set(op->ptr, "axis_lock", pso->axislock);
}
-/* handle an event to toggle axis locks - returns whether any change in state is needed */
+/**
+ * Handle an event to toggle axis locks - returns whether any change in state is needed.
+ */
static bool pose_slide_toggle_axis_locks(wmOperator *op,
tPoseSlideOp *pso,
ePoseSlide_AxisLock axis)
@@ -1041,7 +1423,9 @@ static bool pose_slide_toggle_axis_locks(wmOperator *op,
return true;
}
-/* common code for modal() */
+/**
+ * Operator `modal()` callback.
+ */
static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso = op->customdata;
@@ -1051,55 +1435,60 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
const bool has_numinput = hasNumInput(&pso->num);
switch (event->type) {
- case LEFTMOUSE: /* confirm */
+ case LEFTMOUSE: /* Confirm. */
case EVT_RETKEY:
case EVT_PADENTER: {
if (event->val == KM_PRESS) {
- /* return to normal cursor and header status */
+ /* Return to normal cursor and header status. */
+ ED_workspace_status_text(C, NULL);
ED_area_status_text(pso->area, NULL);
WM_cursor_modal_restore(win);
- /* insert keyframes as required... */
+ /* Depsgraph updates + redraws. Redraw needed to remove UI. */
+ pose_slide_refresh(C, pso);
+
+ /* Insert keyframes as required. */
pose_slide_autoKeyframe(C, pso);
pose_slide_exit(op);
- /* done! */
+ /* Done! */
return OPERATOR_FINISHED;
}
break;
}
- case EVT_ESCKEY: /* cancel */
+ case EVT_ESCKEY: /* Cancel. */
case RIGHTMOUSE: {
if (event->val == KM_PRESS) {
- /* return to normal cursor and header status */
+ /* Return to normal cursor and header status. */
+ ED_workspace_status_text(C, NULL);
ED_area_status_text(pso->area, NULL);
WM_cursor_modal_restore(win);
- /* reset transforms back to original state */
+ /* Reset transforms back to original state. */
pose_slide_reset(pso);
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws.*/
pose_slide_refresh(C, pso);
- /* clean up temp data */
+ /* Clean up temp data. */
pose_slide_exit(op);
- /* canceled! */
+ /* Canceled! */
return OPERATOR_CANCELLED;
}
break;
}
- /* Percentage Change... */
- case MOUSEMOVE: /* calculate new position */
+ /* Factor Change... */
+ case MOUSEMOVE: /* Calculate new position. */
{
- /* only handle mousemove if not doing numinput */
+ /* Only handle mouse-move if not doing numinput. */
if (has_numinput == false) {
- /* update percentage based on position of mouse */
- pose_slide_mouse_update_percentage(pso, op, event);
+ /* Update factor based on position of mouse. */
+ pose_slide_mouse_update_factor(pso, op, event);
- /* update pose to reflect the new values (see below) */
+ /* Update pose to reflect the new values (see below). */
do_pose_update = true;
}
break;
@@ -1111,12 +1500,12 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Grab percentage from numeric input, and store this new value for redo
* NOTE: users see ints, while internally we use a 0-1 float
*/
- value = pso->percentage * 100.0f;
+ value = pso->factor * 100.0f;
applyNumInput(&pso->num, &value);
- pso->percentage = value / 100.0f;
- CLAMP(pso->percentage, 0.0f, 1.0f);
- RNA_float_set(op->ptr, "percentage", pso->percentage);
+ pso->factor = value / 100.0f;
+ CLAMP(pso->factor, 0.0f, 1.0f);
+ RNA_float_set(op->ptr, "factor", pso->factor);
/* Update pose to reflect the new values (see below) */
do_pose_update = true;
@@ -1178,13 +1567,61 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
+ /* Overshoot. */
+ case EVT_EKEY: {
+ pso->overshoot = !pso->overshoot;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Precision mode. */
+ case EVT_LEFTSHIFTKEY:
+ case EVT_RIGHTSHIFTKEY: {
+ pso->precision = true;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Increments mode. */
+ case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY: {
+ pso->increments = true;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Toggle Bone visibility. */
+ case EVT_HKEY: {
+ View3D *v3d = pso->area->spacedata.first;
+ v3d->overlay.flag ^= V3D_OVERLAY_HIDE_BONES;
+ }
+
default: /* Some other unhandled key... */
break;
}
}
+ /* Precision and stepping only active while button is held. */
+ else if (event->val == KM_RELEASE) {
+ switch (event->type) {
+ case EVT_LEFTSHIFTKEY:
+ case EVT_RIGHTSHIFTKEY: {
+ pso->precision = false;
+ do_pose_update = true;
+ break;
+ }
+ case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY: {
+ pso->increments = false;
+ do_pose_update = true;
+ break;
+ }
+ default:
+ break;
+ }
+ }
else {
- /* unhandled event - maybe it was some view manipulation? */
- /* allow to pass through */
+ /* Unhandled event - maybe it was some view manipulation? */
+ /* Allow to pass through. */
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
}
}
@@ -1193,13 +1630,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Perform pose updates - in response to some user action
* (e.g. pressing a key or moving the mouse). */
if (do_pose_update) {
- /* update percentage indicator in header */
- pose_slide_draw_status(pso);
+ /* Update percentage indicator in header. */
+ pose_slide_draw_status(C, pso);
- /* reset transforms (to avoid accumulation errors) */
+ /* Reset transforms (to avoid accumulation errors). */
pose_slide_reset(pso);
- /* apply... */
+ /* Apply. */
if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) {
pose_slide_apply(C, pso);
}
@@ -1208,21 +1645,25 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- /* still running... */
+ /* Still running. */
return OPERATOR_RUNNING_MODAL;
}
-/* common code for cancel() */
+/**
+ * Common code for cancel()
+ */
static void pose_slide_cancel(bContext *UNUSED(C), wmOperator *op)
{
- /* cleanup and done */
+ /* Cleanup and done. */
pose_slide_exit(op);
}
-/* common code for exec() methods */
+/**
+ * Common code for exec() methods.
+ */
static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
{
- /* settings should have been set up ok for applying, so just apply! */
+ /* Settings should have been set up ok for applying, so just apply! */
if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) {
pose_slide_apply(C, pso);
}
@@ -1230,10 +1671,10 @@ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso
pose_slide_rest_pose_apply(C, pso);
}
- /* insert keyframes if needed */
+ /* Insert keyframes if needed. */
pose_slide_autoKeyframe(C, pso);
- /* cleanup and done */
+ /* Cleanup and done. */
pose_slide_exit(op);
return OPERATOR_FINISHED;
@@ -1297,12 +1738,14 @@ static void pose_slide_opdef_properties(wmOperatorType *ot)
/* ------------------------------------ */
-/* invoke() - for 'push from breakdown' mode */
+/**
+ * Operator `invoke()` callback for 'push from breakdown' mode.
+ */
static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1310,19 +1753,23 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
- /* do common setup work */
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
+
+ /* Do common setup work. */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for push */
+/**
+ * Operator `exec()` callback - for push.
+ */
static int pose_slide_push_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1330,7 +1777,7 @@ static int pose_slide_push_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1349,7 +1796,7 @@ void POSE_OT_push(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1357,12 +1804,14 @@ void POSE_OT_push(wmOperatorType *ot)
/* ........................ */
-/* invoke() - for 'relax to breakdown' mode */
+/**
+ * Invoke callback - for 'relax to breakdown' mode.
+ */
static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1370,19 +1819,23 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
- /* do common setup work */
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
+
+ /* Do common setup work. */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for relax */
+/**
+ * Operator exec() - for relax.
+ */
static int pose_slide_relax_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1390,7 +1843,7 @@ static int pose_slide_relax_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1409,19 +1862,21 @@ void POSE_OT_relax(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
}
/* ........................ */
-/* invoke() - for 'push from rest pose' mode */
+/**
+ * Operator `invoke()` - for 'push from rest pose' mode.
+ */
static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1429,19 +1884,23 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
+
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
/* do common setup work */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for push */
+/**
+ * Operator `exec()` - for push.
+ */
static int pose_slide_push_rest_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1449,7 +1908,7 @@ static int pose_slide_push_rest_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1468,7 +1927,7 @@ void POSE_OT_push_rest(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1476,12 +1935,14 @@ void POSE_OT_push_rest(wmOperatorType *ot)
/* ........................ */
-/* invoke() - for 'relax' mode */
+/**
+ * Operator `invoke()` - for 'relax' mode.
+ */
static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1489,19 +1950,23 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
- /* do common setup work */
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
+
+ /* Do common setup work. */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for relax */
+/**
+ * Operator `exec()` - for relax.
+ */
static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1509,7 +1974,7 @@ static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1528,7 +1993,7 @@ void POSE_OT_relax_rest(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1536,12 +2001,14 @@ void POSE_OT_relax_rest(wmOperatorType *ot)
/* ........................ */
-/* invoke() - for 'breakdown' mode */
+/**
+ * Operator `invoke()` - for 'breakdown' mode.
+ */
static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1549,19 +2016,23 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
- /* do common setup work */
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
+
+ /* Do common setup work. */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for breakdown */
+/**
+ * Operator exec() - for breakdown.
+ */
static int pose_slide_breakdown_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1569,7 +2040,7 @@ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1588,7 +2059,7 @@ void POSE_OT_breakdown(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1599,36 +2070,39 @@ void POSE_OT_breakdown(wmOperatorType *ot)
/* "termination conditions" - i.e. when we stop */
typedef enum ePosePropagate_Termination {
- /* stop after the current hold ends */
+ /** Stop after the current hold ends. */
POSE_PROPAGATE_SMART_HOLDS = 0,
- /* only do on the last keyframe */
+ /** Only do on the last keyframe. */
POSE_PROPAGATE_LAST_KEY,
- /* stop after the next keyframe */
+ /** Stop after the next keyframe. */
POSE_PROPAGATE_NEXT_KEY,
- /* stop after the specified frame */
+ /** Stop after the specified frame. */
POSE_PROPAGATE_BEFORE_FRAME,
- /* stop when we run out of keyframes */
+ /** Stop when we run out of keyframes. */
POSE_PROPAGATE_BEFORE_END,
- /* only do on keyframes that are selected */
+ /** Only do on keyframes that are selected. */
POSE_PROPAGATE_SELECTED_KEYS,
- /* only do on the frames where markers are selected */
+ /** Only do on the frames where markers are selected. */
POSE_PROPAGATE_SELECTED_MARKERS,
} ePosePropagate_Termination;
-/* Termination data needed for some modes -
- * assumes only one of these entries will be needed at a time. */
+/**
+ * Termination data needed for some modes -
+ * assumes only one of these entries will be needed at a time.
+ */
typedef union tPosePropagate_ModeData {
- /* smart holds + before frame: frame number to stop on */
+ /** Smart holds + before frame: frame number to stop on. */
float end_frame;
- /* selected markers: listbase for CfraElem's marking these frames */
+ /** Selected markers: listbase for CfraElem's marking these frames. */
ListBase sel_markers;
} tPosePropagate_ModeData;
/* --------------------------------- */
-/* get frame on which the "hold" for the bone ends
+/**
+ * Get frame on which the "hold" for the bone ends.
* XXX: this may not really work that well if a bone moves on some channels and not others
* if this happens to be a major issue, scrap this, and just make this happen
* independently per F-Curve
@@ -1642,7 +2116,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
LinkData *ld;
float endFrame = startFrame;
- /* set up optimized data-structures for searching for relevant keyframes + holds */
+ /* Set up optimized data-structures for searching for relevant keyframes + holds. */
BLI_dlrbTree_init(&keys);
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
@@ -1650,7 +2124,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
fcurve_to_keylist(adt, fcu, &keys, 0);
}
- /* find the long keyframe (i.e. hold), and hence obtain the endFrame value
+ /* Find the long keyframe (i.e. hold), and hence obtain the endFrame value
* - the best case would be one that starts on the frame itself
*/
ActKeyColumn *ab = (ActKeyColumn *)BLI_dlrbTree_search_exact(
@@ -1664,67 +2138,68 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
* otherwise forget it, as we'd be overwriting some valid data.
*/
if (ab == NULL) {
- /* we've got case 1, so try the one after */
+ /* We've got case 1, so try the one after. */
ab = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &startFrame);
if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
- /* try the block before this frame then as last resort */
+ /* Try the block before this frame then as last resort. */
ab = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &startFrame);
}
}
- /* whatever happens, stop searching now... */
+ /* Whatever happens, stop searching now.... */
if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
- /* restrict range to just the frame itself
- * i.e. everything is in motion, so no holds to safely overwrite
- */
+ /* Restrict range to just the frame itself
+ * i.e. everything is in motion, so no holds to safely overwrite. */
ab = NULL;
}
- /* check if we can go any further than we've already gone */
+ /* Check if we can go any further than we've already gone. */
if (ab) {
- /* go to next if it is also valid and meets "extension" criteria */
+ /* Go to next if it is also valid and meets "extension" criteria. */
while (ab->next) {
ActKeyColumn *abn = ab->next;
- /* must be valid */
+ /* Must be valid. */
if ((actkeyblock_get_valid_hold(abn) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
break;
}
- /* should have the same number of curves */
+ /* Should have the same number of curves. */
if (ab->totblock != abn->totblock) {
break;
}
- /* we can extend the bounds to the end of this "next" block now */
+ /* We can extend the bounds to the end of this "next" block now. */
ab = abn;
}
- /* end frame can now take the value of the end of the block */
+ /* End frame can now take the value of the end of the block. */
endFrame = ab->next->cfra;
}
- /* free temp memory */
+ /* Free temp memory. */
BLI_dlrbTree_free(&keys);
- /* return the end frame we've found */
+ /* Return the end frame we've found. */
return endFrame;
}
-/* get reference value from F-Curve using RNA */
+/**
+ * Get reference value from F-Curve using RNA.
+ */
static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
bool found = false;
- /* base pointer is always the object -> id_ptr */
+ /* Base pointer is always the `object -> id_ptr`. */
RNA_id_pointer_create(&ob->id, &id_ptr);
- /* resolve the property... */
+ /* Resolve the property. */
if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
if (RNA_property_array_check(prop)) {
- /* array */
+ /* Array. */
if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
found = true;
switch (RNA_property_type(prop)) {
@@ -1744,7 +2219,7 @@ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
}
}
else {
- /* not an array */
+ /* Not an array. */
found = true;
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN:
@@ -1769,7 +2244,9 @@ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
return found;
}
-/* propagate just works along each F-Curve in turn */
+/**
+ * Propagate just works along each F-Curve in turn.
+ */
static void pose_propagate_fcurve(
wmOperator *op, Object *ob, FCurve *fcu, float startFrame, tPosePropagate_ModeData modeData)
{
@@ -1778,31 +2255,31 @@ static void pose_propagate_fcurve(
BezTriple *bezt;
float refVal = 0.0f;
bool keyExists;
- int i, match;
+ int i;
bool first = true;
- /* skip if no keyframes to edit */
+ /* Skip if no keyframes to edit. */
if ((fcu->bezt == NULL) || (fcu->totvert < 2)) {
return;
}
- /* find the reference value from bones directly, which means that the user
+ /* Find the reference value from bones directly, which means that the user
* doesn't need to firstly keyframe the pose (though this doesn't mean that
- * they can't either)
- */
+ * they can't either). */
if (!pose_propagate_get_refVal(ob, fcu, &refVal)) {
return;
}
- /* find the first keyframe to start propagating from
+ /* Find the first keyframe to start propagating from:
* - if there's a keyframe on the current frame, we probably want to save this value there too
- * since it may be as of yet unkeyed
+ * since it may be as of yet un-keyed
* - if starting before the starting frame, don't touch the key, as it may have had some valid
* values
* - if only doing selected keyframes, start from the first one
*/
if (mode != POSE_PROPAGATE_SELECTED_KEYS) {
- match = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, startFrame, fcu->totvert, &keyExists);
+ const int match = BKE_fcurve_bezt_binarysearch_index(
+ fcu->bezt, startFrame, fcu->totvert, &keyExists);
if (fcu->bezt[match].vec[1][0] < startFrame) {
i = match + 1;
@@ -1812,58 +2289,58 @@ static void pose_propagate_fcurve(
}
}
else {
- /* selected - start from first keyframe */
+ /* Selected - start from first keyframe. */
i = 0;
}
for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
- /* additional termination conditions based on the operator 'mode' property go here... */
+ /* Additional termination conditions based on the operator 'mode' property go here. */
if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
- /* stop if keyframe is outside the accepted range */
+ /* Stop if keyframe is outside the accepted range. */
if (bezt->vec[1][0] > modeData.end_frame) {
break;
}
}
else if (mode == POSE_PROPAGATE_NEXT_KEY) {
- /* stop after the first keyframe has been processed */
+ /* Stop after the first keyframe has been processed. */
if (first == false) {
break;
}
}
else if (mode == POSE_PROPAGATE_LAST_KEY) {
- /* only affect this frame if it will be the last one */
+ /* Only affect this frame if it will be the last one. */
if (i != (fcu->totvert - 1)) {
continue;
}
}
else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
- /* only allow if there's a marker on this frame */
+ /* Only allow if there's a marker on this frame. */
CfraElem *ce = NULL;
- /* stop on matching marker if there is one */
+ /* Stop on matching marker if there is one. */
for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
if (ce->cfra == round_fl_to_int(bezt->vec[1][0])) {
break;
}
}
- /* skip this keyframe if no marker */
+ /* Skip this keyframe if no marker. */
if (ce == NULL) {
continue;
}
}
else if (mode == POSE_PROPAGATE_SELECTED_KEYS) {
- /* only allow if this keyframe is already selected - skip otherwise */
+ /* Only allow if this keyframe is already selected - skip otherwise. */
if (BEZT_ISSEL_ANY(bezt) == 0) {
continue;
}
}
- /* just flatten handles, since values will now be the same either side... */
+ /* Just flatten handles, since values will now be the same either side. */
/* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */
bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal;
- /* select keyframe to indicate that it's been changed */
+ /* Select keyframe to indicate that it's been changed. */
bezt->f2 |= SELECT;
first = false;
}
@@ -1883,7 +2360,7 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
tPosePropagate_ModeData modeData;
const int mode = RNA_enum_get(op->ptr, "mode");
- /* isolate F-Curves related to the selected bones */
+ /* Isolate F-Curves related to the selected bones. */
poseAnim_mapping_get(C, &pflinks);
if (BLI_listbase_is_empty(&pflinks)) {
@@ -1894,42 +2371,41 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* mode-specific data preprocessing (requiring no access to curves) */
+ /* Mode-specific data preprocessing (requiring no access to curves). */
if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
- /* get a list of selected markers */
+ /* Get a list of selected markers. */
ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
}
else {
- /* assume everything else wants endFrame */
+ /* Assume everything else wants endFrame. */
modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
}
- /* for each bone, perform the copying required */
+ /* For each bone, perform the copying required. */
for (pfl = pflinks.first; pfl; pfl = pfl->next) {
LinkData *ld;
- /* mode-specific data preprocessing (requiring access to all curves) */
+ /* Mode-specific data preprocessing (requiring access to all curves). */
if (mode == POSE_PROPAGATE_SMART_HOLDS) {
- /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
- * from the keyframe that occurs after the current frame
- */
+ /* We store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
+ * from the keyframe that occurs after the current frame. */
modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)CFRA);
}
- /* go through propagating pose to keyframes, curve by curve */
+ /* Go through propagating pose to keyframes, curve by curve. */
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)CFRA, modeData);
}
}
- /* free temp data */
+ /* Free temp data. */
poseAnim_mapping_free(&pflinks);
if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
BLI_freelistN(&modeData.sel_markers);
}
- /* updates + notifiers */
+ /* Updates + notifiers. */
FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
poseAnim_mapping_refresh(C, scene, ob);
}
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index 8d1c196e9c2..43ab20eb71c 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -172,9 +172,6 @@ static void applyarmature_transfer_properties(EditBone *curbone,
unit_qt(pchan->quat);
unit_axis_angle(pchan->rotAxis, &pchan->rotAngle);
pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f;
-
- /* Set anim lock. */
- curbone->flag |= BONE_UNKEYED;
}
/* Adjust the current edit position of the bone using the pose space matrix. */
@@ -1200,10 +1197,6 @@ static int pose_clear_transform_generic_exec(bContext *C,
/* do auto-keyframing as appropriate */
if (autokeyframe_cfra_can_key(scene, &ob_iter->id)) {
- /* clear any unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
/* tag for autokeying later */
ANIM_relative_keyingset_add_source(&dsources, &ob_iter->id, &RNA_PoseBone, pchan);
@@ -1212,12 +1205,6 @@ static int pose_clear_transform_generic_exec(bContext *C,
clear_func(ob_iter->pose, pchan_eval);
#endif
}
- else {
- /* add unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag |= BONE_UNKEYED;
- }
- }
}
FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c
index c75e9c9ef69..75348c2b196 100644
--- a/source/blender/editors/armature/pose_utils.c
+++ b/source/blender/editors/armature/pose_utils.c
@@ -313,11 +313,6 @@ void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks,
/* Add data-source override for the PoseChannel, to be used later. */
ANIM_relative_keyingset_add_source(&dsources, &pfl->ob->id, &RNA_PoseBone, pchan);
-
- /* clear any unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
}
/* insert keyframes for all relevant bones in one go */
diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h
index 704e4b740de..8ecf41162e9 100644
--- a/source/blender/editors/curve/curve_intern.h
+++ b/source/blender/editors/curve/curve_intern.h
@@ -142,7 +142,10 @@ struct GHash *ED_curve_keyindex_hash_duplicate(struct GHash *keyindex);
void ED_curve_keyindex_update_nurb(struct EditNurb *editnurb, struct Nurb *nu, struct Nurb *newnu);
/* helper functions */
-void ed_editnurb_translate_flag(struct ListBase *editnurb, uint8_t flag, const float vec[3]);
+void ed_editnurb_translate_flag(struct ListBase *editnurb,
+ uint8_t flag,
+ const float vec[3],
+ bool is_2d);
bool ed_editnurb_extrude_flag(struct EditNurb *editnurb, const uint8_t flag);
bool ed_editnurb_spin(float viewmat[4][4],
struct View3D *v3d,
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 4816a432376..2999ac784ba 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1314,7 +1314,6 @@ void ED_curve_editnurb_make(Object *obedit)
LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
Nurb *newnu = BKE_nurb_duplicate(nu);
- BKE_nurb_test_2d(newnu); /* after join, or any other creation of curve */
BLI_addtail(&editnurb->nurbs, newnu);
}
@@ -1706,7 +1705,7 @@ static void rotateflagNurb(ListBase *editnurb,
}
}
-void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float vec[3])
+void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float vec[3], bool is_2d)
{
/* all verts with ('flag' & flag) translate */
BezTriple *bezt;
@@ -1741,7 +1740,9 @@ void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float ve
}
}
- BKE_nurb_test_2d(nu);
+ if (is_2d) {
+ BKE_nurb_project_2d(nu);
+ }
}
}
@@ -5401,7 +5402,7 @@ static int ed_editcurve_addvert(Curve *cu,
mul_v3_fl(center, 1.0f / (float)verts_len);
sub_v3_v3v3(ofs, location_init, center);
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
ofs[2] = 0.0f;
}
@@ -5439,7 +5440,7 @@ static int ed_editcurve_addvert(Curve *cu,
copy_v3_v3(location, location_init);
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
location[2] = 0.0f;
}
@@ -5455,10 +5456,6 @@ static int ed_editcurve_addvert(Curve *cu,
nurb_new->orderu = 4;
nurb_new->flag |= CU_SMOOTH;
BKE_nurb_bezierPoints_add(nurb_new, 1);
-
- if ((cu->flag & CU_3D) == 0) {
- nurb_new->flag |= CU_2D;
- }
}
else {
/* Copy the active nurb settings. */
@@ -5579,7 +5576,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
},
mval,
NULL,
@@ -5590,7 +5587,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
ED_transform_snap_object_context_destroy(snap_context);
}
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
const float eps = 1e-6f;
/* get the view vector to 'location' */
@@ -6649,7 +6646,7 @@ void CURVE_OT_dissolve_verts(wmOperatorType *ot)
static bool nurb_bezt_flag_any(const Nurb *nu, const char flag_test)
{
- BezTriple *bezt = nu->bezt;
+ const BezTriple *bezt;
int i;
for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
@@ -6922,9 +6919,9 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
cu = ob_active->data;
BLI_movelisttolist(&cu->nurb, &tempbase);
- if (ob_active->type == OB_CURVE) {
+ if (ob_active->type == OB_CURVE && CU_IS_2D(cu)) {
/* Account for mixed 2D/3D curves when joining */
- BKE_curve_curve_dimension_update(cu);
+ BKE_curve_dimension_update(cu);
}
DEG_relations_tag_update(bmain); /* because we removed object(s), call before editmode! */
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index 5b66d473466..065763764c1 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -380,10 +380,10 @@ Nurb *ED_curve_add_nurbs_primitive(
mul_mat3_m4_v3(mat, vec);
- ed_editnurb_translate_flag(editnurb, SELECT, vec);
+ ed_editnurb_translate_flag(editnurb, SELECT, vec, CU_IS_2D(cu));
ed_editnurb_extrude_flag(cu->editnurb, SELECT);
mul_v3_fl(vec, -2.0f);
- ed_editnurb_translate_flag(editnurb, SELECT, vec);
+ ed_editnurb_translate_flag(editnurb, SELECT, vec, CU_IS_2D(cu));
BLI_remlink(editnurb, nu);
@@ -492,15 +492,13 @@ Nurb *ED_curve_add_nurbs_primitive(
BLI_assert(nu != NULL);
if (nu) { /* should always be set */
- if ((obedit->type != OB_SURF) && ((cu->flag & CU_3D) == 0)) {
- nu->flag |= CU_2D;
- }
-
nu->flag |= CU_SMOOTH;
cu->actnu = BLI_listbase_count(editnurb);
cu->actvert = CU_ACT_NONE;
- BKE_nurb_test_2d(nu);
+ if (CU_IS_2D(cu)) {
+ BKE_nurb_project_2d(nu);
+ }
}
return nu;
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 48a36ff276d..03c120df28b 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -206,9 +206,11 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd,
else {
const ViewDepths *depths = rv3d->depths;
if (depths && ((uint)mval_i[0] < depths->w) && ((uint)mval_i[1] < depths->h)) {
- const double depth = (double)ED_view3d_depth_read_cached(&cdd->vc, mval_i);
+ float depth_fl = 1.0f;
+ ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl);
+ const double depth = (double)depth_fl;
if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
- if (ED_view3d_depth_unproject(region, mval_i, depth, r_location_world)) {
+ if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) {
is_location_world_set = true;
if (r_normal_world) {
zero_v3(r_normal_world);
@@ -385,7 +387,6 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C),
GPU_matrix_translate_3f(selem->location_local[0] - location_prev[0],
selem->location_local[1] - location_prev[1],
selem->location_local[2] - location_prev[2]);
- location_prev = selem->location_local;
const float radius = stroke_elem_radius(cdd, selem);
@@ -1072,7 +1073,7 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const float *plane_no = NULL;
const float *plane_co = NULL;
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
/* 2D overrides other options */
plane_co = obedit->obmat[3];
plane_no = obedit->obmat[2];
@@ -1083,13 +1084,8 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* needed or else the draw matrix can be incorrect */
view3d_operator_needs_opengl(C);
- ED_view3d_autodist_init(cdd->vc.depsgraph, cdd->vc.region, cdd->vc.v3d, 0);
-
- if (cdd->vc.rv3d->depths) {
- cdd->vc.rv3d->depths->damaged = true;
- }
-
- ED_view3d_depth_update(cdd->vc.region);
+ ED_view3d_depth_override(
+ cdd->vc.depsgraph, cdd->vc.region, cdd->vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, true);
if (cdd->vc.rv3d->depths != NULL) {
cdd->project.use_depth = true;
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
index e3fc8b73172..90cefef38ee 100644
--- a/source/blender/editors/curve/editcurve_select.c
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -1990,7 +1990,7 @@ static int edcu_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
ED_object_base_activate(C, basact);
}
- DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(obedit->data, ID_RECALC_SELECT | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index 80771df3572..95970cff4ef 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -1573,7 +1573,8 @@ static int delete_exec(bContext *C, wmOperator *op)
else if (*sel >= range[1]) {
*sel -= len_remove;
}
- else if (*sel < range[1]) {
+ else {
+ BLI_assert(*sel < range[1]);
/* pass */
*sel = range[0];
}
diff --git a/source/blender/editors/geometry/geometry_attributes.c b/source/blender/editors/geometry/geometry_attributes.c
index 12f6bb90677..b2ecee90a57 100644
--- a/source/blender/editors/geometry/geometry_attributes.c
+++ b/source/blender/editors/geometry/geometry_attributes.c
@@ -52,12 +52,16 @@ static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return DummyRNA_NULL_items;
+ }
+
Object *ob = ED_object_context(C);
- if (ob != NULL) {
- return rna_enum_attribute_domain_itemf(ob->data, r_free);
+ if (ob == NULL) {
+ return DummyRNA_NULL_items;
}
- return DummyRNA_NULL_items;
+ return rna_enum_attribute_domain_itemf(ob->data, r_free);
}
static int geometry_attribute_add_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
index f4c4e4eb2ac..d99ce25451c 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
@@ -320,14 +320,44 @@ static int gizmo_button2d_cursor_get(wmGizmo *gz)
return WM_CURSOR_DEFAULT;
}
-static void gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
+static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
{
ScrArea *area = CTX_wm_area(C);
float rad = CIRCLE_RESOLUTION * U.dpi_fac / 2.0f;
- r_bounding_box->xmin = gz->matrix_basis[3][0] + area->totrct.xmin - rad;
- r_bounding_box->ymin = gz->matrix_basis[3][1] + area->totrct.ymin - rad;
- r_bounding_box->xmax = r_bounding_box->xmin + rad;
- r_bounding_box->ymax = r_bounding_box->ymin + rad;
+ const float *co = NULL;
+ float matrix_final[4][4];
+ float co_proj[3];
+ WM_gizmo_calc_matrix_final(gz, matrix_final);
+
+ if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
+ ARegion *region = CTX_wm_region(C);
+ if (ED_view3d_project_float_global(region, matrix_final[3], co_proj, V3D_PROJ_TEST_NOP) ==
+ V3D_PROJ_RET_OK) {
+ float matrix_final_no_offset[4][4];
+ const RegionView3D *rv3d = region->regiondata;
+ WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
+ const float factor = ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final_no_offset[3]) /
+ ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final[3]);
+ /* It's possible (although unlikely) `matrix_final_no_offset` is behind the view.
+ * `matrix_final` has already been projected so both can't be negative. */
+ if (factor > 0.0f) {
+ rad *= factor;
+ }
+ co = co_proj;
+ }
+ }
+ else {
+ co = matrix_final[3];
+ }
+
+ if (co != NULL) {
+ r_bounding_box->xmin = co[0] + area->totrct.xmin - rad;
+ r_bounding_box->ymin = co[1] + area->totrct.ymin - rad;
+ r_bounding_box->xmax = r_bounding_box->xmin + rad;
+ r_bounding_box->ymax = r_bounding_box->ymin + rad;
+ return true;
+ }
+ return false;
}
static void gizmo_button2d_free(wmGizmo *gz)
diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
index 364444f99ae..68322ed56af 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -292,7 +292,7 @@ static int gizmo_move_modal(bContext *C,
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
.use_occlusion_test = true,
},
mval_fl,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
index b8ee1722cb3..b2d3a2e1576 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
@@ -27,11 +27,14 @@
* \brief Snap gizmo which exposes the location, normal and index in the props.
*/
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "DNA_scene_types.h"
#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
#include "GPU_immediate.h"
#include "GPU_state.h"
@@ -57,11 +60,6 @@
typedef struct SnapGizmo3D {
wmGizmo gizmo;
- PropertyRNA *prop_prevpoint;
- PropertyRNA *prop_location;
- PropertyRNA *prop_normal;
- PropertyRNA *prop_elem_index;
- PropertyRNA *prop_snap_force;
/* We could have other snap contexts, for now only support 3D view. */
SnapObjectContext *snap_context_v3d;
@@ -70,7 +68,9 @@ typedef struct SnapGizmo3D {
struct {
int x;
int y;
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
short shift, ctrl, alt, oskey;
+#endif
} last_eventstate;
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
@@ -78,8 +78,18 @@ typedef struct SnapGizmo3D {
int snap_on;
bool invert_snap;
#endif
- int use_snap_override;
+
+ /* Setup. */
+ eSnapGizmo flag;
+ float *prevpoint;
+ float prevpoint_stack[3];
+ short snap_elem_force;
+
+ /* Return values. */
short snap_elem;
+ float loc[3];
+ float nor[3];
+ int elem_index[3];
/** Enabled when snap is activated, even if it didn't find anything. */
bool is_enabled;
@@ -91,28 +101,30 @@ static bool eventstate_has_changed(SnapGizmo3D *snap_gizmo, const wmWindowManage
if (wm && wm->winactive) {
const wmEvent *event = wm->winactive->eventstate;
if ((event->x != snap_gizmo->last_eventstate.x) ||
- (event->y != snap_gizmo->last_eventstate.y) ||
- (event->ctrl != snap_gizmo->last_eventstate.ctrl) ||
- (event->shift != snap_gizmo->last_eventstate.shift) ||
- (event->alt != snap_gizmo->last_eventstate.alt) ||
- (event->oskey != snap_gizmo->last_eventstate.oskey)) {
+ (event->y != snap_gizmo->last_eventstate.y)) {
return true;
}
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) {
+ if ((event->ctrl != snap_gizmo->last_eventstate.ctrl) ||
+ (event->shift != snap_gizmo->last_eventstate.shift) ||
+ (event->alt != snap_gizmo->last_eventstate.alt) ||
+ (event->oskey != snap_gizmo->last_eventstate.oskey)) {
+ return true;
+ }
+ }
+#endif
}
return false;
}
/* Copies the current eventstate. */
-static void eventstate_save(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
+static void eventstate_save_xy(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
{
if (wm && wm->winactive) {
const wmEvent *event = wm->winactive->eventstate;
snap_gizmo->last_eventstate.x = event->x;
snap_gizmo->last_eventstate.y = event->y;
- snap_gizmo->last_eventstate.ctrl = event->ctrl;
- snap_gizmo->last_eventstate.shift = event->shift;
- snap_gizmo->last_eventstate.alt = event->alt;
- snap_gizmo->last_eventstate.oskey = event->oskey;
}
}
@@ -132,11 +144,12 @@ static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
return snap_gizmo->invert_snap;
}
- if (snap_gizmo->keymap == NULL) {
- /* Lazy initialization. */
- snap_gizmo->keymap = WM_modalkeymap_find(wm->defaultconf, "Generic Gizmo Tweak Modal Map");
- RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on);
- }
+ /* Save new eventstate. */
+ snap_gizmo->last_eventstate.ctrl = event->ctrl;
+ snap_gizmo->last_eventstate.shift = event->shift;
+ snap_gizmo->last_eventstate.alt = event->alt;
+ snap_gizmo->last_eventstate.oskey = event->oskey;
+
const int snap_on = snap_gizmo->snap_on;
wmKeyMap *keymap = WM_keymap_active(wm, snap_gizmo->keymap);
@@ -158,6 +171,19 @@ static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
}
#endif
+static short snap_gizmo_snap_elements(SnapGizmo3D *snap_gizmo)
+{
+ int snap_elements = snap_gizmo->snap_elem_force;
+
+ wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(&snap_gizmo->gizmo, "snap_elements");
+ if (gz_prop->prop) {
+ snap_elements |= RNA_property_enum_get(&gz_prop->ptr, gz_prop->prop);
+ }
+ snap_elements &= (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
+ SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR);
+ return (ushort)snap_elements;
+}
+
/* -------------------------------------------------------------------- */
/** \name ED_gizmo_library specific API
* \{ */
@@ -265,26 +291,32 @@ SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene,
return snap_gizmo->snap_context_v3d;
}
-bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz)
+void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag)
{
-#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
- return snap_gizmo->invert_snap;
-#else
- return false;
-#endif
+ snap_gizmo->flag |= flag;
}
-void ED_gizmotypes_snap_3d_toggle_set(wmGizmo *gz, bool enable)
+void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, eSnapGizmo flag)
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
- snap_gizmo->use_snap_override = (int)enable;
+ snap_gizmo->flag &= ~flag;
}
-void ED_gizmotypes_snap_3d_toggle_clear(wmGizmo *gz)
+bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, eSnapGizmo flag)
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
- snap_gizmo->use_snap_override = -1;
+ return (snap_gizmo->flag & flag) != 0;
+}
+
+bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz)
+{
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
+ return snap_gizmo->invert_snap;
+#else
+ return false;
+#endif
}
bool ED_gizmotypes_snap_3d_is_enabled(wmGizmo *gz)
@@ -298,29 +330,17 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
const ARegion *region,
const View3D *v3d,
const wmWindowManager *wm,
- const float mval_fl[2],
- float r_loc[3],
- float r_nor[3])
+ const float mval_fl[2])
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
snap_gizmo->is_enabled = false;
- if (snap_gizmo->use_snap_override != -1) {
- if (snap_gizmo->use_snap_override == false) {
- snap_gizmo->snap_elem = 0;
- return 0;
- }
- }
-
-#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
- snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm);
-#endif
-
- eventstate_save(snap_gizmo, wm);
Scene *scene = DEG_get_input_scene(depsgraph);
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
- if (snap_gizmo->use_snap_override == -1) {
+ if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) {
+ snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm);
+
const ToolSettings *ts = scene->toolsettings;
if (snap_gizmo->invert_snap != !(ts->snap_flag & SCE_SNAP)) {
snap_gizmo->snap_elem = 0;
@@ -328,6 +348,7 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
}
}
#endif
+ eventstate_save_xy(snap_gizmo, wm);
snap_gizmo->is_enabled = true;
@@ -336,43 +357,50 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
int snap_elem_index[3] = {-1, -1, -1};
int index = -1;
- wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "snap_elements");
- int snap_elements = RNA_property_enum_get(&gz_prop->ptr, gz_prop->prop);
- if (gz_prop->prop != snap_gizmo->prop_snap_force) {
- int snap_elements_force = RNA_property_enum_get(gz->ptr, snap_gizmo->prop_snap_force);
- snap_elements |= snap_elements_force;
- }
- snap_elements &= (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
- SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR);
+ ushort snap_elements = snap_gizmo_snap_elements(snap_gizmo);
if (snap_elements) {
float prev_co[3] = {0.0f};
- if (RNA_property_is_set(gz->ptr, snap_gizmo->prop_prevpoint)) {
- RNA_property_float_get_array(gz->ptr, snap_gizmo->prop_prevpoint, prev_co);
+ if (snap_gizmo->prevpoint) {
+ copy_v3_v3(prev_co, snap_gizmo->prevpoint);
}
else {
snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR;
}
+ eSnapSelect snap_select = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_ONLY_ACTIVE) ?
+ SNAP_ONLY_ACTIVE :
+ SNAP_ALL;
+
+ eSnapEditType edit_mode_type = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL) ?
+ SNAP_GEOM_FINAL :
+ (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ?
+ SNAP_GEOM_CAGE :
+ SNAP_GEOM_EDIT;
+
+ bool use_occlusion_test = (snap_gizmo->flag & ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE) ? false :
+ true;
+
float dist_px = 12.0f * U.pixelsize;
ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, gz);
- snap_elem = ED_transform_snap_object_project_view3d_ex(snap_gizmo->snap_context_v3d,
- depsgraph,
- snap_elements,
- &(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
- .use_occlusion_test = true,
- },
- mval_fl,
- prev_co,
- &dist_px,
- co,
- no,
- &index,
- NULL,
- NULL);
+ snap_elem = ED_transform_snap_object_project_view3d_ex(
+ snap_gizmo->snap_context_v3d,
+ depsgraph,
+ snap_elements,
+ &(const struct SnapObjectParams){
+ .snap_select = snap_select,
+ .edit_mode_type = edit_mode_type,
+ .use_occlusion_test = use_occlusion_test,
+ },
+ mval_fl,
+ prev_co,
+ &dist_px,
+ co,
+ no,
+ &index,
+ NULL,
+ NULL);
}
if (snap_elem == 0) {
@@ -392,45 +420,166 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
}
snap_gizmo->snap_elem = snap_elem;
- RNA_property_float_set_array(gz->ptr, snap_gizmo->prop_location, co);
- RNA_property_float_set_array(gz->ptr, snap_gizmo->prop_normal, no);
- RNA_property_int_set_array(gz->ptr, snap_gizmo->prop_elem_index, snap_elem_index);
+ copy_v3_v3(snap_gizmo->loc, co);
+ copy_v3_v3(snap_gizmo->nor, no);
+ copy_v3_v3_int(snap_gizmo->elem_index, snap_elem_index);
+
+ return snap_elem;
+}
+void ED_gizmotypes_snap_3d_data_get(
+ wmGizmo *gz, float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem)
+{
+ SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
+ BLI_assert(snap_gizmo->is_enabled);
if (r_loc) {
- copy_v3_v3(r_loc, co);
+ copy_v3_v3(r_loc, snap_gizmo->loc);
}
-
if (r_nor) {
- copy_v3_v3(r_nor, no);
+ copy_v3_v3(r_nor, snap_gizmo->nor);
+ }
+ if (r_elem_index) {
+ copy_v3_v3_int(r_elem_index, snap_gizmo->elem_index);
+ }
+ if (r_snap_elem) {
+ *r_snap_elem = snap_gizmo->snap_elem;
}
-
- return snap_elem;
}
/** \} */
/* -------------------------------------------------------------------- */
-/** \name GIZMO_GT_snap_3d
+/** \name RNA callbacks
* \{ */
-static void snap_gizmo_setup(wmGizmo *gz)
+/* Based on 'rna_GizmoProperties_find_operator'. */
+static struct SnapGizmo3D *gizmo_snap_rna_find_operator(PointerRNA *ptr)
{
- SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
+ IDProperty *properties = ptr->data;
+ for (bScreen *screen = G_MAIN->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ if (area->spacetype != SPACE_VIEW3D) {
+ continue;
+ }
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ if (region->regiontype == RGN_TYPE_WINDOW && region->gizmo_map) {
+ wmGizmoMap *gzmap = region->gizmo_map;
+ LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, WM_gizmomap_group_list(gzmap)) {
+ LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
+ if (gz->properties == properties) {
+ return (SnapGizmo3D *)gz;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+static int gizmo_snap_rna_snap_elements_force_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop))
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ return snap_gizmo->snap_elem_force;
+ }
+ return 0;
+}
+
+static void gizmo_snap_rna_snap_elements_force_set_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ int value)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ snap_gizmo->snap_elem_force = (short)value;
+ }
+}
+
+static void gizmo_snap_rna_prevpoint_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3(values, snap_gizmo->prevpoint_stack);
+ }
+}
- /* For quick access to the props. */
- snap_gizmo->prop_prevpoint = RNA_struct_find_property(gz->ptr, "prev_point");
- snap_gizmo->prop_location = RNA_struct_find_property(gz->ptr, "location");
- snap_gizmo->prop_normal = RNA_struct_find_property(gz->ptr, "normal");
- snap_gizmo->prop_elem_index = RNA_struct_find_property(gz->ptr, "snap_elem_index");
- snap_gizmo->prop_snap_force = RNA_struct_find_property(gz->ptr, "snap_elements_force");
+static void gizmo_snap_rna_prevpoint_set_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ const float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ if (values) {
+ copy_v3_v3(snap_gizmo->prevpoint_stack, values);
+ snap_gizmo->prevpoint = snap_gizmo->prevpoint_stack;
+ }
+ else {
+ snap_gizmo->prevpoint = NULL;
+ }
+ }
+}
- snap_gizmo->use_snap_override = -1;
+static void gizmo_snap_rna_location_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3(values, snap_gizmo->loc);
+ }
+}
- /* Prop fallback. */
- WM_gizmo_target_property_def_rna(gz, "snap_elements", gz->ptr, "snap_elements_force", -1);
+static void gizmo_snap_rna_location_set_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ const float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3(snap_gizmo->loc, values);
+ }
+}
- /* Flags. */
+static void gizmo_snap_rna_normal_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3(values, snap_gizmo->nor);
+ }
+}
+
+static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ int *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3_int(values, snap_gizmo->elem_index);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GIZMO_GT_snap_3d
+ * \{ */
+
+static void snap_gizmo_setup(wmGizmo *gz)
+{
gz->flag |= WM_GIZMO_NO_TOOLTIP;
+
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
+ snap_gizmo->keymap = WM_modalkeymap_find(gz->parent_gzgroup->type->keyconf,
+ "Generic Gizmo Tweak Modal Map");
+ RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on);
+#endif
}
static void snap_gizmo_draw(const bContext *C, wmGizmo *gz)
@@ -455,42 +604,50 @@ static void snap_gizmo_draw(const bContext *C, wmGizmo *gz)
return;
}
- float location[3], prev_point_stack[3], *prev_point = NULL;
uchar color_line[4], color_point[4];
-
- RNA_property_float_get_array(gz->ptr, snap_gizmo->prop_location, location);
-
UI_GetThemeColor3ubv(TH_TRANSFORM, color_line);
color_line[3] = 128;
rgba_float_to_uchar(color_point, gz->color);
- if (RNA_property_is_set(gz->ptr, snap_gizmo->prop_prevpoint)) {
- RNA_property_float_get_array(gz->ptr, snap_gizmo->prop_prevpoint, prev_point_stack);
- prev_point = prev_point_stack;
- }
-
GPU_line_smooth(false);
GPU_line_width(1.0f);
+
+ const float *prev_point = (snap_gizmo_snap_elements(snap_gizmo) &
+ SCE_SNAP_MODE_EDGE_PERPENDICULAR) ?
+ snap_gizmo->prevpoint :
+ NULL;
+
ED_gizmotypes_snap_3d_draw_util(
- rv3d, prev_point, location, NULL, color_line, color_point, snap_gizmo->snap_elem);
+ rv3d, prev_point, snap_gizmo->loc, NULL, color_line, color_point, snap_gizmo->snap_elem);
}
static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
wmWindowManager *wm = CTX_wm_manager(C);
+ ARegion *region = CTX_wm_region(C);
+
+ /* FIXME: this hack is to ignore drag events, otherwise drag events
+ * cause momentary snap gizmo re-positioning at the drag-start location, see: T87511. */
+ if (wm && wm->winactive) {
+ const wmEvent *event = wm->winactive->eventstate;
+ int mval_compare[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin};
+ if (!equals_v2v2_int(mval_compare, mval)) {
+ return snap_gizmo->snap_elem ? 0 : -1;
+ }
+ }
+
if (!eventstate_has_changed(snap_gizmo, wm)) {
/* Performance, do not update. */
return snap_gizmo->snap_elem ? 0 : -1;
}
- ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
const float mval_fl[2] = {UNPACK2(mval)};
short snap_elem = ED_gizmotypes_snap_3d_update(
- gz, CTX_data_ensure_evaluated_depsgraph(C), region, v3d, wm, mval_fl, NULL, NULL);
+ gz, CTX_data_ensure_evaluated_depsgraph(C), region, v3d, wm, mval_fl);
if (snap_elem) {
ED_region_tag_redraw_editor_overlays(region);
@@ -553,57 +710,74 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
}
/* Setup. */
- RNA_def_enum_flag(gzt->srna,
- "snap_elements_force",
- rna_enum_snap_element_items,
- SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE,
- "Snap Elements",
- "");
-
- RNA_def_float_vector(gzt->srna,
- "prev_point",
- 3,
- NULL,
- FLT_MIN,
- FLT_MAX,
- "Previous Point",
- "Point that defines the location of the perpendicular snap",
- FLT_MIN,
- FLT_MAX);
+ PropertyRNA *prop;
+ prop = RNA_def_enum_flag(gzt->srna,
+ "snap_elements_force",
+ rna_enum_snap_element_items,
+ SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE,
+ "Snap Elements",
+ "");
+
+ RNA_def_property_enum_funcs_runtime(prop,
+ gizmo_snap_rna_snap_elements_force_get_fn,
+ gizmo_snap_rna_snap_elements_force_set_fn,
+ NULL);
+
+ prop = RNA_def_float_array(gzt->srna,
+ "prev_point",
+ 3,
+ NULL,
+ FLT_MIN,
+ FLT_MAX,
+ "Previous Point",
+ "Point that defines the location of the perpendicular snap",
+ FLT_MIN,
+ FLT_MAX);
+
+ RNA_def_property_float_array_funcs_runtime(
+ prop, gizmo_snap_rna_prevpoint_get_fn, gizmo_snap_rna_prevpoint_set_fn, NULL);
/* Returns. */
- RNA_def_float_vector(gzt->srna,
- "location",
- 3,
- NULL,
- FLT_MIN,
- FLT_MAX,
- "Location",
- "Snap Point Location",
- FLT_MIN,
- FLT_MAX);
-
- RNA_def_float_vector(gzt->srna,
- "normal",
- 3,
- NULL,
- FLT_MIN,
- FLT_MAX,
- "Normal",
- "Snap Point Normal",
- FLT_MIN,
- FLT_MAX);
-
- RNA_def_int_vector(gzt->srna,
- "snap_elem_index",
- 3,
- NULL,
- INT_MIN,
- INT_MAX,
- "Snap Element",
- "Array index of face, edge and vert snapped",
- INT_MIN,
- INT_MAX);
+ prop = RNA_def_float_translation(gzt->srna,
+ "location",
+ 3,
+ NULL,
+ FLT_MIN,
+ FLT_MAX,
+ "Location",
+ "Snap Point Location",
+ FLT_MIN,
+ FLT_MAX);
+
+ RNA_def_property_float_array_funcs_runtime(
+ prop, gizmo_snap_rna_location_get_fn, gizmo_snap_rna_location_set_fn, NULL);
+
+ prop = RNA_def_float_vector_xyz(gzt->srna,
+ "normal",
+ 3,
+ NULL,
+ FLT_MIN,
+ FLT_MAX,
+ "Normal",
+ "Snap Point Normal",
+ FLT_MIN,
+ FLT_MAX);
+
+ RNA_def_property_float_array_funcs_runtime(prop, gizmo_snap_rna_normal_get_fn, NULL, NULL);
+
+ prop = RNA_def_int_vector(gzt->srna,
+ "snap_elem_index",
+ 3,
+ NULL,
+ INT_MIN,
+ INT_MAX,
+ "Snap Element",
+ "Array index of face, edge and vert snapped",
+ INT_MIN,
+ INT_MAX);
+
+ RNA_def_property_int_array_funcs_runtime(
+ prop, gizmo_snap_rna_snap_elem_index_get_fn, NULL, NULL);
/* Read/Write. */
WM_gizmotype_target_property_def(gzt, "snap_elements", PROP_ENUM, 1);
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index 47ae90acb74..bff7310e9f7 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -36,10 +36,12 @@ set(SRC
annotate_paint.c
drawgpencil.c
editaction_gpencil.c
+ gpencil_add_blank.c
gpencil_add_lineart.c
gpencil_add_monkey.c
gpencil_add_stroke.c
gpencil_armature.c
+ gpencil_bake_animation.c
gpencil_convert.c
gpencil_data.c
gpencil_edit.c
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index e9817f82090..c155587e95a 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -660,10 +660,14 @@ static short annotation_stroke_addpoint(tGPsdata *p,
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph,
- p->region,
- v3d,
- (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* convert screen-coordinates to appropriate coordinates (and store them) */
@@ -1222,7 +1226,7 @@ static void annotation_stroke_doeraser(tGPsdata *p)
if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph, p->region, v3d, 0);
+ ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
}
}
@@ -1544,7 +1548,7 @@ static void annotation_paint_initstroke(tGPsdata *p,
if (p->gpl == NULL) {
/* tag for annotations */
p->gpd->flag |= GP_DATA_ANNOTATIONS;
- p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true, false);
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
@@ -1695,8 +1699,14 @@ static void annotation_paint_strokeend(tGPsdata *p)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(
- p->depsgraph, p->region, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* check if doing eraser or not */
diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c
new file mode 100644
index 00000000000..3aa16e54597
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_add_blank.c
@@ -0,0 +1,101 @@
+/*
+ * 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
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "BLI_math.h"
+#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 "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_gpencil.h"
+
+/* Definition of the most important info from a color */
+typedef struct ColorTemplate {
+ const char *name;
+ float line[4];
+ float fill[4];
+} ColorTemplate;
+
+/* Add color an ensure duplications (matched by name) */
+static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct)
+{
+ int index;
+ Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index);
+
+ copy_v4_v4(ma->gp_style->stroke_rgba, pct->line);
+ srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba);
+
+ copy_v4_v4(ma->gp_style->fill_rgba, pct->fill);
+ srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba);
+
+ return index;
+}
+
+/* ***************************************************************** */
+/* Stroke Geometry */
+
+/* ***************************************************************** */
+/* Color Data */
+
+static const ColorTemplate gp_stroke_material_black = {
+ "Black",
+ {0.0f, 0.0f, 0.0f, 1.0f},
+ {0.0f, 0.0f, 0.0f, 0.0f},
+};
+
+/* ***************************************************************** */
+/* Blank API */
+
+/* Add a Simple empty object with one layer and one color. */
+void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4]))
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ /* create colors */
+ int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black);
+
+ /* set first color as active and in brushes */
+ ob->actcol = color_black + 1;
+
+ /* layers */
+ bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
+
+ /* frames */
+ BKE_gpencil_frame_addnew(layer, CFRA);
+
+ /* update depsgraph */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+}
diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c
index 6b28c6ec13e..ac0da0ad1db 100644
--- a/source/blender/editors/gpencil/gpencil_add_lineart.c
+++ b/source/blender/editors/gpencil/gpencil_add_lineart.c
@@ -96,7 +96,7 @@ void ED_gpencil_create_lineart(bContext *C, Object *ob)
ob->actcol = color_black + 1;
/* layers */
- bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
BKE_gpencil_frame_addnew(lines, 0);
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 4497d963c6d..d8734c4ae6b 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -837,8 +837,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
/* layers */
/* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */
- bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false);
- bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false, false);
+ bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
/* NOTE: No need to check for existing, as this will take care of it for us */
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index 26237636526..e95496b51ee 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -225,8 +225,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
ob->actcol = color_black + 1;
/* layers */
- bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false);
- bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false, false);
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA);
diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c
new file mode 100644
index 00000000000..30ebc9189c5
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_bake_animation.c
@@ -0,0 +1,448 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_anim_data.h"
+#include "BKE_context.h"
+#include "BKE_duplilist.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_layer.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "ED_gpencil.h"
+#include "ED_transform_snap_object_context.h"
+
+#include "gpencil_intern.h"
+
+const EnumPropertyItem rna_gpencil_reproject_type_items[] = {
+ {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""},
+ {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
+ {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
+ {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
+ {GP_REPROJECT_VIEW,
+ "VIEW",
+ 0,
+ "View",
+ "Reproject the strokes to end up on the same plane, as if drawn from the current "
+ "viewpoint "
+ "using 'Cursor' Stroke Placement"},
+ {GP_REPROJECT_CURSOR,
+ "CURSOR",
+ 0,
+ "Cursor",
+ "Reproject the strokes using the orientation of 3D cursor"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+/* Check frame_end is always > start frame! */
+static void gpencil_bake_set_frame_end(struct Main *UNUSED(main),
+ struct Scene *UNUSED(scene),
+ struct PointerRNA *ptr)
+{
+ int frame_start = RNA_int_get(ptr, "frame_start");
+ int frame_end = RNA_int_get(ptr, "frame_end");
+
+ if (frame_end <= frame_start) {
+ RNA_int_set(ptr, "frame_end", frame_start + 1);
+ }
+}
+
+/* Extract mesh animation to Grease Pencil. */
+static bool gpencil_bake_grease_pencil_animation_poll(bContext *C)
+{
+ Object *obact = CTX_data_active_object(C);
+ if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) {
+ return false;
+ }
+
+ /* Check if grease pencil or empty for dupli groups. */
+ if ((obact == NULL) || ((obact->type != OB_GPENCIL) && (obact->type != OB_EMPTY))) {
+ return false;
+ }
+
+ /* Only if the current view is 3D View. */
+ ScrArea *area = CTX_wm_area(C);
+ return (area && area->spacetype);
+}
+
+typedef struct GpBakeOb {
+ struct GpBakeOb *next, *prev;
+ Object *ob;
+} GpBakeOb;
+
+/* Get list of keyframes used by selected objects. */
+static void animdata_keyframe_list_get(ListBase *ob_list,
+ const bool only_selected,
+ GHash *r_keyframes)
+{
+ /* Loop all objects to get the list of keyframes used. */
+ LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) {
+ Object *ob = elem->ob;
+ AnimData *adt = BKE_animdata_from_id(&ob->id);
+ if ((adt == NULL) || (adt->action == NULL)) {
+ continue;
+ }
+ LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) {
+ int i;
+ BezTriple *bezt;
+ for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) {
+ /* Keyframe number is x value of point. */
+ if ((bezt->f2 & SELECT) || (!only_selected)) {
+ /* Insert only one key for each keyframe number. */
+ int key = (int)bezt->vec[1][0];
+ if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
+ }
+ }
+ }
+ }
+}
+
+static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+ ListBase *lb;
+ DupliObject *dob;
+ lb = object_duplilist(depsgraph, scene, ob);
+ for (dob = lb->first; dob; dob = dob->next) {
+ if (dob->ob->type != OB_GPENCIL) {
+ continue;
+ }
+
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = dob->ob;
+ BLI_addtail(list, elem);
+ }
+
+ free_object_duplilist(lb);
+}
+
+static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+
+ /* Add active object. In some files this could not be in selected array. */
+ Object *obact = CTX_data_active_object(C);
+
+ if (obact->type == OB_GPENCIL) {
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = obact;
+ BLI_addtail(list, elem);
+ }
+ /* Add duplilist. */
+ else if (obact->type == OB_EMPTY) {
+ gpencil_bake_duplilist(depsgraph, scene, obact, list);
+ }
+
+ /* Add other selected objects. */
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if (ob == obact) {
+ continue;
+ }
+ /* Add selected objects.*/
+ if (ob->type == OB_GPENCIL) {
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = ob;
+ BLI_addtail(list, elem);
+ }
+
+ /* Add duplilist. */
+ if (ob->type == OB_EMPTY) {
+ gpencil_bake_duplilist(depsgraph, scene, ob, list);
+ }
+ }
+ CTX_DATA_END;
+}
+
+static void gpencil_bake_free_ob_list(ListBase *list)
+{
+ LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) {
+ MEM_SAFE_FREE(elem);
+ }
+}
+
+static int gpencil_bake_grease_pencil_animation_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);
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+
+ ListBase ob_selected_list = {NULL, NULL};
+ gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list);
+
+ /* Grab all relevant settings. */
+ const int step = RNA_int_get(op->ptr, "step");
+
+ const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ?
+ scene->r.sfra :
+ RNA_int_get(op->ptr, "frame_start");
+
+ const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ?
+ scene->r.efra :
+ RNA_int_get(op->ptr, "frame_end");
+
+ const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
+ const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start;
+ const int project_type = RNA_enum_get(op->ptr, "project_type");
+
+ /* Create a new grease pencil object. */
+ Object *ob_gpencil = NULL;
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits);
+ float invmat[4][4];
+ invert_m4_m4(invmat, ob_gpencil->obmat);
+
+ bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data;
+ gpd_dst->draw_mode = GP_DRAWMODE_2D;
+
+ /* Set cursor to indicate working. */
+ WM_cursor_wait(true);
+
+ GP_SpaceConversion gsc = {NULL};
+ SnapObjectContext *sctx = NULL;
+ if (project_type != GP_REPROJECT_KEEP) {
+ /* Init space conversion stuff. */
+ gpencil_point_conversion_init(C, &gsc);
+ /* Move the grease pencil object to conversion data. */
+ gsc.ob = ob_gpencil;
+
+ /* Init snap context for geometry projection. */
+ sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C));
+ }
+
+ /* Loop all frame range. */
+ int oldframe = (int)DEG_get_ctime(depsgraph);
+ int key = -1;
+
+ /* Get list of keyframes. */
+ GHash *keyframe_list = BLI_ghash_int_new(__func__);
+ if (only_selected) {
+ animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list);
+ }
+
+ for (int i = frame_start; i < frame_end + 1; i++) {
+ key++;
+ /* Jump if not step limit but include last frame always. */
+ if ((key % step != 0) && (i != frame_end)) {
+ continue;
+ }
+
+ /* Check if frame is in the list of frames to be exported. */
+ if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) {
+ continue;
+ }
+
+ /* Move scene to new frame. */
+ CFRA = i;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+
+ /* Loop all objects in the list. */
+ LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) {
+ Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob);
+ bGPdata *gpd_src = ob_eval->data;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) {
+ /* Create destination layer. */
+ char *layer_name;
+ layer_name = BLI_sprintfN("%s_%s", elem->ob->id.name + 2, gpl_src->info);
+ bGPDlayer *gpl_dst = BKE_gpencil_layer_named_get(gpd_dst, layer_name);
+ if (gpl_dst == NULL) {
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, layer_name, true, false);
+ }
+ MEM_freeN(layer_name);
+
+ /* Layer Transform matrix. */
+ float matrix[4][4];
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix);
+
+ /* Duplicate frame. */
+ bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(gpl_src, CFRA, GP_GETFRAME_USE_PREV);
+ if (gpf_src == NULL) {
+ continue;
+ }
+ bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true);
+ gpf_dst->framenum = CFRA + frame_offset;
+ gpf_dst->flag &= ~GP_FRAME_SELECT;
+ BLI_addtail(&gpl_dst->frames, gpf_dst);
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) {
+ /* Create material of the stroke. */
+ Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1);
+ bool found = false;
+ for (int index = 0; index < ob_gpencil->totcol; index++) {
+ Material *ma_dst = BKE_object_material_get(ob_gpencil, index + 1);
+ if (ma_src == ma_dst) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ BKE_object_material_slot_add(bmain, ob_gpencil);
+ BKE_object_material_assign(
+ bmain, ob_gpencil, ma_src, ob_gpencil->totcol, BKE_MAT_ASSIGN_USERPREF);
+ }
+
+ /* Set new material index. */
+ gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src);
+
+ /* Update point location to new object space. */
+ for (int j = 0; j < gps->totpoints; j++) {
+ bGPDspoint *pt = &gps->points[j];
+ mul_m4_v3(matrix, &pt->x);
+ mul_m4_v3(invmat, &pt->x);
+ }
+
+ /* Reproject stroke. */
+ if (project_type != GP_REPROJECT_KEEP) {
+ ED_gpencil_stroke_reproject(
+ depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false);
+ }
+ else {
+ BKE_gpencil_stroke_geometry_update(gpd_dst, gps);
+ }
+ }
+ }
+ }
+ }
+ /* Return scene frame state and DB to original state. */
+ CFRA = oldframe;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+
+ /* Free memory. */
+ gpencil_bake_free_ob_list(&ob_selected_list);
+ if (sctx != NULL) {
+ ED_transform_snap_object_context_destroy(sctx);
+ }
+ /* Free temp hash table. */
+ if (keyframe_list != NULL) {
+ BLI_ghash_free(keyframe_list, NULL, NULL);
+ }
+
+ /* Notifiers. */
+ DEG_relations_tag_update(bmain);
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ DEG_id_tag_update(&gpd_dst->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+
+ /* Reset cursor. */
+ WM_cursor_wait(false);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+static int gpencil_bake_grease_pencil_animation_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ PropertyRNA *prop;
+ Scene *scene = CTX_data_scene(C);
+
+ prop = RNA_struct_find_property(op->ptr, "frame_start");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ const int frame_start = RNA_property_int_get(op->ptr, prop);
+ if (frame_start < scene->r.sfra) {
+ RNA_property_int_set(op->ptr, prop, scene->r.sfra);
+ }
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "frame_end");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ const int frame_end = RNA_property_int_get(op->ptr, prop);
+ if (frame_end > scene->r.efra) {
+ RNA_property_int_set(op->ptr, prop, scene->r.efra);
+ }
+ }
+
+ /* Show popup dialog to allow editing. */
+ return WM_operator_props_dialog_popup(C, op, 250);
+}
+
+void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Bake Object Transform to Grease Pencil";
+ ot->idname = "GPENCIL_OT_bake_grease_pencil_animation";
+ ot->description = "Bake grease pencil object transform to grease pencil keyframes";
+
+ /* callbacks */
+ ot->invoke = gpencil_bake_grease_pencil_animation_invoke;
+ ot->exec = gpencil_bake_grease_pencil_animation_exec;
+ ot->poll = gpencil_bake_grease_pencil_animation_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_int(
+ ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000);
+
+ prop = RNA_def_int(
+ ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000);
+ RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end);
+
+ prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100);
+
+ RNA_def_boolean(
+ ot->srna, "only_selected", 0, "Only Selected Keyframes", "Convert only selected keyframes");
+ RNA_def_int(
+ ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
+
+ RNA_def_enum(ot->srna,
+ "project_type",
+ rna_gpencil_reproject_type_items,
+ GP_REPROJECT_KEEP,
+ "Projection Type",
+ "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index ac75ae44c8a..8ab413e907c 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1857,7 +1857,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op)
/* Add layer and frame. */
bGPdata *gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false);
bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA);
done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask);
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index fd2758c8a08..d9a807d17ab 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -129,7 +129,7 @@ static int gpencil_data_add_exec(bContext *C, wmOperator *op)
gpd->flag |= GP_DATA_ANNOTATIONS;
/* add new layer (i.e. a "note") */
- BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false);
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -231,7 +231,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
/* mark as annotation */
(*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS;
- BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false);
gpd = *gpd_ptr;
}
else {
@@ -239,7 +239,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
if ((ob != NULL) && (ob->type == OB_GPENCIL)) {
gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
/* Add a new frame to make it visible in Dopesheet. */
if (gpl != NULL) {
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
@@ -524,7 +524,6 @@ enum {
static bool gpencil_layer_duplicate_object_poll(bContext *C)
{
- ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
return false;
@@ -537,90 +536,75 @@ static bool gpencil_layer_duplicate_object_poll(bContext *C)
return false;
}
- /* check there are more grease pencil objects */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if ((base->object != ob) && (base->object->type == OB_GPENCIL)) {
- return true;
- }
- }
-
- return false;
+ return true;
}
static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "object", name);
-
- if (name[0] == '\0') {
- return OPERATOR_CANCELLED;
- }
-
- Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name);
-
- int mode = RNA_enum_get(op->ptr, "mode");
+ const bool only_active = RNA_boolean_get(op->ptr, "only_active");
+ const int mode = RNA_enum_get(op->ptr, "mode");
Object *ob_src = CTX_data_active_object(C);
bGPdata *gpd_src = (bGPdata *)ob_src->data;
- bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src);
+ bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd_src);
- /* Sanity checks. */
- if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) {
- return OPERATOR_CANCELLED;
- }
- /* Cannot copy itself and check destination type. */
- if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) {
- return OPERATOR_CANCELLED;
- }
-
- bGPdata *gpd_dst = (bGPdata *)ob_dst->data;
-
- /* Create new layer. */
- bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true);
- /* Need to copy some variables (not all). */
- gpl_dst->onion_flag = gpl_src->onion_flag;
- gpl_dst->thickness = gpl_src->thickness;
- gpl_dst->line_change = gpl_src->line_change;
- copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
- gpl_dst->opacity = gpl_src->opacity;
-
- /* Create all frames. */
- LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
-
- if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) {
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if ((ob == ob_src) || (ob->type != OB_GPENCIL)) {
continue;
}
+ bGPdata *gpd_dst = (bGPdata *)ob->data;
+ LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl_src, &gpd_src->layers) {
+ if ((only_active) && (gpl_src != gpl_active)) {
+ continue;
+ }
+ /* Create new layer (adding at head of the list). */
+ bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true, true);
+ /* Need to copy some variables (not all). */
+ gpl_dst->onion_flag = gpl_src->onion_flag;
+ gpl_dst->thickness = gpl_src->thickness;
+ gpl_dst->line_change = gpl_src->line_change;
+ copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
+ gpl_dst->opacity = gpl_src->opacity;
+
+ /* Create all frames. */
+ LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
+
+ if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) {
+ continue;
+ }
- /* Create new frame. */
- bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
+ /* Create new frame. */
+ bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
- /* Copy strokes. */
- LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
+ /* Copy strokes. */
+ LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
- /* Make copy of source stroke. */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
+ /* Make copy of source stroke. */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
- /* Check if material is in destination object,
- * otherwise add the slot with the material. */
- Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1);
- if (ma_src != NULL) {
- int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
+ /* Check if material is in destination object,
+ * otherwise add the slot with the material. */
+ Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1);
+ if (ma_src != NULL) {
+ int idx = BKE_gpencil_object_material_ensure(bmain, ob, ma_src);
- /* Reassign the stroke material to the right slot in destination object. */
- gps_dst->mat_nr = idx;
- }
+ /* Reassign the stroke material to the right slot in destination object. */
+ gps_dst->mat_nr = idx;
+ }
- /* Add new stroke to frame. */
- BLI_addtail(&gpf_dst->strokes, gps_dst);
+ /* Add new stroke to frame. */
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
+ }
+ }
}
+ /* notifiers */
+ DEG_id_tag_update(&gpd_dst->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
+ CTX_DATA_END;
- /* notifiers */
- DEG_id_tag_update(&gpd_dst->id,
- ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
- DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -628,6 +612,8 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
static const EnumPropertyItem copy_mode[] = {
{GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""},
{GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""},
@@ -637,7 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
/* identifiers */
ot->name = "Duplicate Layer to New Object";
ot->idname = "GPENCIL_OT_layer_duplicate_object";
- ot->description = "Make a copy of the active Grease Pencil layer to new object";
+ ot->description = "Make a copy of the active Grease Pencil layer to selected object";
/* callbacks */
ot->exec = gpencil_layer_duplicate_object_exec;
@@ -646,11 +632,14 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- ot->prop = RNA_def_string(
- ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object");
- RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ ot->prop = RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", "");
- RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", "");
+ prop = RNA_def_boolean(ot->srna,
+ "only_active",
+ true,
+ "Only Active",
+ "Copy only active Layer, uncheck to append all layers");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* ********************* Duplicate Frame ************************** */
@@ -1443,7 +1432,7 @@ static int gpencil_layer_change_exec(bContext *C, wmOperator *op)
/* Get layer or create new one */
if (layer_num == -1) {
/* Create layer */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
else {
/* Try to get layer */
@@ -1555,6 +1544,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
const int direction = RNA_enum_get(op->ptr, "direction");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ bGPDstroke *gps_target = NULL;
bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -1569,7 +1559,6 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
if (gpf == NULL) {
continue;
}
- bool gpf_lock = false;
/* verify if any selected stroke is in the extreme of the stack and select to move */
for (gps = gpf->strokes.first; gps; gps = gps->next) {
/* only if selected */
@@ -1582,18 +1571,19 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
+ bool gpf_lock = false;
/* some stroke is already at front*/
if (ELEM(direction, GP_STROKE_MOVE_TOP, GP_STROKE_MOVE_UP)) {
if (gps == gpf->strokes.last) {
gpf_lock = true;
- continue;
+ gps_target = gps;
}
}
/* Some stroke is already at bottom. */
if (ELEM(direction, GP_STROKE_MOVE_BOTTOM, GP_STROKE_MOVE_DOWN)) {
if (gps == gpf->strokes.first) {
gpf_lock = true;
- continue;
+ gps_target = gps;
}
}
/* add to list (if not locked) */
@@ -1602,47 +1592,74 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
}
}
}
+
+ const int target_index = (gps_target) ? BLI_findindex(&gpf->strokes, gps_target) : -1;
+ int prev_index = target_index;
/* Now do the movement of the stroke */
- if (!gpf_lock) {
- switch (direction) {
- /* Bring to Front */
- case GP_STROKE_MOVE_TOP:
- LISTBASE_FOREACH (LinkData *, link, &selected) {
- gps = link->data;
- BLI_remlink(&gpf->strokes, gps);
+ switch (direction) {
+ /* Bring to Front */
+ case GP_STROKE_MOVE_TOP:
+ LISTBASE_FOREACH (LinkData *, link, &selected) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ if (gps_target) {
+ BLI_insertlinkbefore(&gpf->strokes, gps_target, gps);
+ }
+ else {
BLI_addtail(&gpf->strokes, gps);
- changed = true;
}
- break;
- /* Bring Forward */
- case GP_STROKE_MOVE_UP:
- LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
- gps = link->data;
- BLI_listbase_link_move(&gpf->strokes, gps, 1);
- changed = true;
+ changed = true;
+ }
+ break;
+ /* Bring Forward */
+ case GP_STROKE_MOVE_UP:
+ LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
+ gps = link->data;
+ if (gps_target) {
+ int gps_index = BLI_findindex(&gpf->strokes, gps);
+ if (gps_index + 1 >= prev_index) {
+ prev_index = gps_index;
+ continue;
+ }
+ prev_index = gps_index;
+ }
+ BLI_listbase_link_move(&gpf->strokes, gps, 1);
+ changed = true;
+ }
+ break;
+ /* Send Backward */
+ case GP_STROKE_MOVE_DOWN:
+ LISTBASE_FOREACH (LinkData *, link, &selected) {
+ gps = link->data;
+ if (gps_target) {
+ int gps_index = BLI_findindex(&gpf->strokes, gps);
+ if (gps_index - 1 <= prev_index) {
+ prev_index = gps_index;
+ continue;
+ }
+ prev_index = gps_index;
}
- break;
- /* Send Backward */
- case GP_STROKE_MOVE_DOWN:
- LISTBASE_FOREACH (LinkData *, link, &selected) {
- gps = link->data;
- BLI_listbase_link_move(&gpf->strokes, gps, -1);
- changed = true;
+ BLI_listbase_link_move(&gpf->strokes, gps, -1);
+ changed = true;
+ }
+ break;
+ /* Send to Back */
+ case GP_STROKE_MOVE_BOTTOM:
+ LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ if (gps_target) {
+ BLI_insertlinkafter(&gpf->strokes, gps_target, gps);
}
- break;
- /* Send to Back */
- case GP_STROKE_MOVE_BOTTOM:
- LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
- gps = link->data;
- BLI_remlink(&gpf->strokes, gps);
+ else {
BLI_addhead(&gpf->strokes, gps);
- changed = true;
}
- break;
- default:
- BLI_assert(0);
- break;
- }
+ changed = true;
+ }
+ break;
+ default:
+ BLI_assert(0);
+ break;
}
BLI_freelistN(&selected);
}
@@ -3561,6 +3578,79 @@ void GPENCIL_OT_set_active_material(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* ********************* Append Materials in a new object ************************** */
+static bool gpencil_materials_copy_to_object_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ short *totcolp = BKE_object_material_len_p(ob);
+ if (*totcolp == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static int gpencil_materials_copy_to_object_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ const bool only_active = RNA_boolean_get(op->ptr, "only_active");
+ Object *ob_src = CTX_data_active_object(C);
+ Material *ma_active = BKE_gpencil_material(ob_src, ob_src->actcol);
+
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if ((ob == ob_src) || (ob->type != OB_GPENCIL)) {
+ continue;
+ }
+ /* Duplicate materials. */
+ for (int i = 0; i < ob_src->totcol; i++) {
+ Material *ma_src = BKE_object_material_get(ob_src, i + 1);
+ if (only_active && ma_src != ma_active) {
+ continue;
+ }
+
+ if (ma_src != NULL) {
+ BKE_gpencil_object_material_ensure(bmain, ob, ma_src);
+ }
+ }
+
+ /* notifiers */
+ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Copy Materials to Selected Object";
+ ot->idname = "GPENCIL_OT_materials_copy_to_object";
+ ot->description = "Append Materials of the active Grease Pencil to other object";
+
+ /* callbacks */
+ ot->exec = gpencil_materials_copy_to_object_exec;
+ ot->poll = gpencil_materials_copy_to_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_boolean(ot->srna,
+ "only_active",
+ true,
+ "Only Active",
+ "Append only active material, uncheck to append all materials");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
/* Parent GPencil object to Lattice */
bool ED_gpencil_add_lattice_modifier(const bContext *C,
ReportList *reports,
@@ -3717,3 +3807,51 @@ void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot)
ot->exec = gpencil_layer_mask_remove_exec;
ot->poll = gpencil_active_layer_poll;
}
+
+static int gpencil_layer_mask_move_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ const int direction = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl)) {
+ return OPERATOR_CANCELLED;
+ }
+ if (gpl->act_mask > 0) {
+ bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1);
+ if (mask != NULL) {
+ BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
+ if (BLI_listbase_link_move(&gpl->mask_layers, mask, direction)) {
+ gpl->act_mask += direction;
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_layer_mask_move(wmOperatorType *ot)
+{
+ static const EnumPropertyItem slot_move[] = {
+ {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
+ {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Move Grease Pencil Layer Mask";
+ ot->idname = "GPENCIL_OT_layer_mask_move";
+ ot->description = "Move the active Grease Pencil mask layer up/down in the list";
+
+ /* api callbacks */
+ ot->exec = gpencil_layer_mask_move_exec;
+ ot->poll = gpencil_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 4bbd475dd2c..f29f5187015 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -1677,7 +1677,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
if (gpl == NULL) {
/* no active layer - let's just create one */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
BKE_report(
@@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- Scene *scene = CTX_data_scene(C);
bGPDlayer *target_layer = NULL;
ListBase strokes = {NULL, NULL};
int layer_num = RNA_int_get(op->ptr, "layer");
const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
- return OPERATOR_CANCELLED;
- }
-
- /* if autolock enabled, disabled now */
+ /* If autolock enabled, disabled now. */
if (use_autolock) {
gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
}
@@ -1840,7 +1835,7 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
}
else {
/* Create a new layer. */
- target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true);
+ target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
}
if (target_layer == NULL) {
@@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* Extract all strokes to move to this layer
- * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
- * getting repeatedly moved
- */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = gpl->actframe;
-
- /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
- if ((gpl == target_layer) || (gpf == NULL)) {
+ /* Extract all strokes to move to this layer. */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) {
+ /* Skip if this is the layer we're moving strokes to. */
+ if (gpl_src == target_layer) {
continue;
}
+ bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe;
+ for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) {
+ if ((gpf_src == gpl_src->actframe) ||
+ ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf_src == NULL) {
+ continue;
+ }
- /* make copies of selected strokes, and deselect these once we're done */
- LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDstroke *gpsn = NULL;
+ BLI_listbase_clear(&strokes);
+ for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* Check if the color is editable. */
+ if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) {
+ continue;
+ }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
+ if (gps->flag & GP_STROKE_SELECT) {
+ BLI_remlink(&gpf_src->strokes, gps);
+ BLI_addtail(&strokes, gps);
+ }
+ }
+ /* Paste them all in one go. */
+ if (strokes.first) {
+ bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get(
+ target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW);
- /* Check if the color is editable. */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
- continue;
+ BLI_movelisttolist(&gpf_dst->strokes, &strokes);
+ BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
+ }
}
-
- /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
- if (gps->flag & GP_STROKE_SELECT) {
- BLI_remlink(&gpf->strokes, gps);
- BLI_addtail(&strokes, gps);
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
}
}
-
- /* if new layer and autolock, lock old layer */
+ /* If new layer and autolock, lock old layer. */
if ((layer_num == -1) && (use_autolock)) {
- gpl->flag |= GP_LAYER_LOCKED;
+ gpl_src->flag |= GP_LAYER_LOCKED;
}
}
CTX_DATA_END;
- /* Paste them all in one go */
- if (strokes.first) {
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW);
-
- BLI_movelisttolist(&gpf->strokes, &strokes);
- BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
- }
-
/* back autolock status */
if (use_autolock) {
gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
@@ -2778,7 +2779,7 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
/* Poll callback for snap operators */
/* NOTE: For now, we only allow these in the 3D view, as other editors do not
- * define a cursor or gridstep which can be used
+ * define a cursor or grid-step which can be used.
*/
static bool gpencil_snap_poll(bContext *C)
{
@@ -3442,7 +3443,7 @@ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
{GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
{GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
{GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
- {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"},
+ {GP_STROKE_CAPS_TOGGLE_DEFAULT, "DEFAULT", 0, "Default", "Set as default rounded"},
{0, NULL, 0, NULL, NULL},
};
@@ -3762,6 +3763,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Init snap context for geometry projection. */
SnapObjectContext *sctx = NULL;
@@ -3774,36 +3776,55 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
int cfra_prv = INT_MIN;
/* Go through each editable + selected stroke, adjusting each of its points one by one... */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- bool curve_select = false;
- if (is_curve_edit && gps->editcurve != NULL) {
- curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
- }
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- if (gps->flag & GP_STROKE_SELECT || curve_select) {
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
+ }
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ bool curve_select = false;
+ if (is_curve_edit && gps->editcurve != NULL) {
+ curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
+ }
- /* update frame to get the new location of objects */
- if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) {
- cfra_prv = gpf_->framenum;
- CFRA = gpf_->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph);
- }
+ if (gps->flag & GP_STROKE_SELECT || curve_select) {
- ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original);
+ /* update frame to get the new location of objects */
+ if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
+ cfra_prv = gpf->framenum;
+ CFRA = gpf->framenum;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+ }
- if (is_curve_edit && gps->editcurve != NULL) {
- BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
- /* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
+ ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
+ if (is_curve_edit && gps->editcurve != NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ /* Update the selection from the stroke to the curve. */
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
- changed = true;
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+
+ changed = true;
+ }
+ }
+ }
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ CTX_DATA_END;
/* return frame state and DB to original state */
CFRA = oldframe;
@@ -3832,7 +3853,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
"VIEW",
0,
"View",
- "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
+ "Reproject the strokes to end up on the same plane, as if drawn from the current "
+ "viewpoint "
"using 'Cursor' Stroke Placement"},
{GP_REPROJECT_SURFACE,
"SURFACE",
@@ -3851,7 +3873,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
ot->name = "Reproject Strokes";
ot->idname = "GPENCIL_OT_reproject";
ot->description =
- "Reproject the selected strokes from the current viewpoint as if they had been newly drawn "
+ "Reproject the selected strokes from the current viewpoint as if they had been newly "
+ "drawn "
"(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
"or for matching deforming geometry)";
@@ -3951,7 +3974,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
if (smooth_thickness) {
/* thickness need to repeat process several times */
- for (int r2 = 0; r2 < r * 20; r2++) {
+ for (int r2 = 0; r2 < 20; r2++) {
BKE_gpencil_stroke_smooth_thickness(gps, i, factor);
}
}
@@ -3981,6 +4004,11 @@ static int gpencil_count_subdivision_cuts(bGPDstroke *gps)
}
}
+ if ((gps->flag & GP_STROKE_CYCLIC) && (gps->points[0].flag & GP_SPOINT_SELECT) &&
+ (gps->points[gps->totpoints - 1].flag & GP_SPOINT_SELECT)) {
+ totnewpoints++;
+ }
+
return totnewpoints;
}
@@ -4079,6 +4107,47 @@ static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts)
}
}
}
+
+ /* Subdivide between last and first point. */
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ bGPDspoint *pt = &temp_points[oldtotpoints - 1];
+ bGPDspoint *next = &temp_points[0];
+ if ((pt->flag & GP_SPOINT_SELECT) && (next->flag & GP_SPOINT_SELECT)) {
+ bGPDspoint *pt_final = &gps->points[i2];
+ if (gps->dvert != NULL) {
+ dvert_final = &gps->dvert[i2];
+ }
+ /* Interpolate all values */
+ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
+ pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
+ CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
+ pt_final->time = interpf(pt->time, next->time, 0.5f);
+ pt_final->flag |= GP_SPOINT_SELECT;
+
+ /* interpolate weights */
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[oldtotpoints - 1];
+ dvert_next = &temp_dverts[0];
+ dvert_final = &gps->dvert[i2];
+
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = MEM_dupallocN(dvert->dw);
+
+ /* interpolate weight values */
+ for (int d = 0; d < dvert->totweight; d++) {
+ MDeformWeight *dw_a = &dvert->dw[d];
+ if (dvert_next->totweight > d) {
+ MDeformWeight *dw_b = &dvert_next->dw[d];
+ MDeformWeight *dw_final = &dvert_final->dw[d];
+ dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
+ }
+ }
+ }
+ }
+ }
+
/* free temp memory */
MEM_SAFE_FREE(temp_points);
MEM_SAFE_FREE(temp_dverts);
@@ -4162,7 +4231,8 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
ot->name = "Subdivide Stroke";
ot->idname = "GPENCIL_OT_stroke_subdivide";
ot->description =
- "Subdivide between continuous selected points of the stroke adding a point half way between "
+ "Subdivide between continuous selected points of the stroke adding a point half way "
+ "between "
"them";
/* api callbacks */
@@ -4474,6 +4544,9 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
+
/* sanity checks */
if (ELEM(NULL, gpd_src)) {
return OPERATOR_CANCELLED;
@@ -4484,8 +4557,22 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
+ /* Cancel if nothing selected. */
+ if (ELEM(mode, GP_SEPARATE_POINT, GP_SEPARATE_STROKE)) {
+ bool has_selected = false;
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ if (ED_gpencil_layer_has_selected_stroke(gpl, is_multiedit)) {
+ has_selected = true;
+ break;
+ }
+ }
+ CTX_DATA_END;
+
+ if (!has_selected) {
+ BKE_report(op->reports, RPT_ERROR, "Nothing selected");
+ return OPERATOR_CANCELLED;
+ }
+ }
/* Create a new object. */
/* Take into account user preferences for duplicating actions. */
@@ -4530,7 +4617,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
if (gps->flag & GP_STROKE_SELECT) {
/* add layer if not created before */
if (gpl_dst == NULL) {
- gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false);
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
}
/* add frame if not created before */
@@ -4979,17 +5066,27 @@ static int gpencil_cutter_lasso_select(bContext *C,
/* init space conversion stuff */
gpencil_point_conversion_init(C, &gsc);
- /* deselect all strokes first */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- int i;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
+ /* Deselect all strokes. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ int i;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ }
+ }
+ /* if not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
}
- CTX_DATA_END;
/* Select points */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
@@ -5250,3 +5347,181 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Stroke Normalize Operator
+ * \{ */
+
+typedef enum eGP_NormalizeMode {
+ GP_NORMALIZE_THICKNESS = 0,
+ GP_NORMALIZE_OPACITY,
+} eGP_NormalizeMode;
+
+static bool gpencil_stroke_normalize_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (gpd == NULL) {
+ return false;
+ }
+
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+
+ return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
+}
+
+static void gpencil_stroke_normalize_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *row;
+
+ const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "mode", 0, NULL, ICON_NONE);
+
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "value", 0, NULL, ICON_NONE);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "factor", 0, NULL, ICON_NONE);
+ }
+}
+
+static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ /* Sanity checks. */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode");
+ const int value = RNA_int_get(op->ptr, "value");
+ const float factor = RNA_float_get(op->ptr, "factor");
+
+ /* Go through each editable + selected stroke. */
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+
+ if (gpf == NULL) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+
+ bool selected = (is_curve_edit) ? gps->editcurve->flag |= GP_CURVE_SELECT :
+ (gps->flag & GP_STROKE_SELECT);
+ if (!selected) {
+ continue;
+ }
+
+ float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1);
+ /* Fill opacity need to be managed before. */
+ if (mode == GP_NORMALIZE_OPACITY) {
+ gps->fill_opacity_fac = factor;
+ CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f);
+ }
+
+ /* Loop all Polyline points. */
+ if (!is_curve_edit) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ pt->strength = factor;
+ CLAMP(pt->strength, 0.0f, 1.0f);
+ }
+ }
+ }
+ else {
+ /* Loop all Bezier points. */
+ for (int i = 0; i < gps->editcurve->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gps->editcurve->curve_points[i];
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ gpc_pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ gpc_pt->strength = factor;
+ CLAMP(gpc_pt->strength, 0.0f, 1.0f);
+ }
+ }
+
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ }
+ /* If not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_normalize(wmOperatorType *ot)
+{
+ static const EnumPropertyItem prop_gpencil_normalize_modes[] = {
+ {GP_NORMALIZE_THICKNESS,
+ "THICKNESS",
+ 0,
+ "Thickness",
+ "Normalizes the stroke thickness by making all points use the same thickness value"},
+ {GP_NORMALIZE_OPACITY,
+ "OPACITY",
+ 0,
+ "Opacity",
+ "Normalizes the stroke opacity by making all points use the same opacity value"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Normalize Stroke";
+ ot->idname = "GPENCIL_OT_stroke_normalize";
+ ot->description = "Normalize stroke attributes";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_normalize_exec;
+ ot->poll = gpencil_stroke_normalize_poll;
+ ot->ui = gpencil_stroke_normalize_ui;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ ot->prop = RNA_def_enum(
+ ot->srna, "mode", prop_gpencil_normalize_modes, 0, "Mode", "Attribute to be normalized");
+ RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f);
+ RNA_def_int(ot->srna, "value", 10, 0, 1000, "Value", "Value", 0, 1000);
+}
+
+/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 4749f40fac5..f74e211dd65 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1029,9 +1029,15 @@ static void gpencil_invert_image(tGPDfill *tgpf)
/* Red->Green */
else if (color[0] == 1.0f) {
set_pixel(ibuf, v, fill_col[1]);
- /* Add thickness of 2 pixels to avoid too thin lines. */
- int offset = (v % ibuf->x < center) ? 1 : -1;
- set_pixel(ibuf, v + offset, fill_col[1]);
+ /* Add thickness of 2 pixels to avoid too thin lines, but avoid extremes of the pixel line.
+ */
+ int row = v / ibuf->x;
+ int lowpix = row * ibuf->x;
+ int highpix = lowpix + ibuf->x - 1;
+ if ((v > lowpix) && (v < highpix)) {
+ int offset = (v % ibuf->x < center) ? 1 : -1;
+ set_pixel(ibuf, v + offset, fill_col[1]);
+ }
}
else {
/* Set to Transparent. */
@@ -1241,6 +1247,7 @@ static bool dilate_shape(ImBuf *ibuf)
static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
{
ImBuf *ibuf;
+ Brush *brush = tgpf->brush;
float rgba[4];
void *lock;
int v[2];
@@ -1273,7 +1280,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
/* Dilate. */
if (dilate) {
- dilate_shape(ibuf);
+ for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) {
+ dilate_shape(ibuf);
+ }
}
for (int idx = imagesize - 1; idx != 0; idx--) {
@@ -1363,7 +1372,8 @@ static void gpencil_get_depth_array(tGPDfill *tgpf)
if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) {
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(tgpf->win, tgpf->region);
- ED_view3d_autodist_init(tgpf->depsgraph, tgpf->region, tgpf->v3d, 0);
+ ED_view3d_depth_override(
+ tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
/* Since strokes are so fine, when using their depth we need a margin
* otherwise they might get missed. */
@@ -1679,7 +1689,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
tgpf->gpd = gpd;
tgpf->gpl = BKE_gpencil_layer_active_get(gpd);
if (tgpf->gpl == NULL) {
- tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true);
+ tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true, false);
}
tgpf->lock_axis = ts->gp_sculpt.lock_axis;
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index c6f74c39beb..276e8c81e0f 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -421,6 +421,7 @@ void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot);
void GPENCIL_OT_layer_mask_add(struct wmOperatorType *ot);
void GPENCIL_OT_layer_mask_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_layer_mask_move(struct wmOperatorType *ot);
void GPENCIL_OT_hide(struct wmOperatorType *ot);
void GPENCIL_OT_reveal(struct wmOperatorType *ot);
@@ -443,6 +444,7 @@ void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot);
void GPENCIL_OT_convert(struct wmOperatorType *ot);
void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot);
+void GPENCIL_OT_bake_grease_pencil_animation(struct wmOperatorType *ot);
void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot);
void GPENCIL_OT_trace_image(struct wmOperatorType *ot);
@@ -486,6 +488,7 @@ void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot);
void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot);
void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot);
@@ -537,6 +540,7 @@ void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot);
void GPENCIL_OT_material_select(struct wmOperatorType *ot);
void GPENCIL_OT_material_set(struct wmOperatorType *ot);
void GPENCIL_OT_set_active_material(struct wmOperatorType *ot);
+void GPENCIL_OT_materials_copy_to_object(struct wmOperatorType *ot);
/* convert old 2.7 files to 2.8 */
void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot);
@@ -748,4 +752,7 @@ struct GP_EditableStrokes_Iter {
} \
(void)0
+/* Reused items for bake operators. */
+extern const EnumPropertyItem rna_gpencil_reproject_type_items[];
+
/* ****************************************************** */
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index bfa1ee6bcaf..0062e363cdf 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -84,7 +84,8 @@ typedef struct tGPDinterpolate_layer {
/** interpolate factor */
float factor;
- /* Hash tablets to create temp relationship between strokes. */
+ /* List of strokes and Hash tablets to create temp relationship between strokes. */
+ struct ListBase selected_strokes;
struct GHash *used_strokes;
struct GHash *pair_strokes;
@@ -282,6 +283,7 @@ static void gpencil_stroke_pair_table(bContext *C,
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Create hash tablets with relationship between strokes. */
+ BLI_listbase_clear(&tgpil->selected_strokes);
tgpil->used_strokes = BLI_ghash_ptr_new(__func__);
tgpil->pair_strokes = BLI_ghash_ptr_new(__func__);
@@ -315,7 +317,8 @@ static void gpencil_stroke_pair_table(bContext *C,
if (ELEM(NULL, gps_from, gps_to)) {
continue;
}
- /* Insert the pair entry in the hash table. */
+ /* Insert the pair entry in the hash table and the list of strokes to keep order. */
+ BLI_addtail(&tgpil->selected_strokes, BLI_genericNodeN(gps_from));
BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to);
}
}
@@ -405,10 +408,13 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
/* Clear previous interpolations. */
gpencil_interpolate_free_tagged_strokes(tgpil->interFrame);
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tgpil->pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
+ LISTBASE_FOREACH (LinkData *, link, &tgpil->selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(tgpil->pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(tgpil->pair_strokes, gps_from);
+
/* Create new stroke. */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
new_stroke->flag |= GP_STROKE_TAG;
@@ -527,10 +533,12 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
gpencil_stroke_pair_table(C, tgpi, tgpil);
/* Create new strokes data with interpolated points reading original stroke. */
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tgpil->pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
+ LISTBASE_FOREACH (LinkData *, link, &tgpil->selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(tgpil->pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(tgpil->pair_strokes, gps_from);
/* If destination stroke is smaller, resize new_stroke to size of gps_to stroke. */
if (gps_from->totpoints > gps_to->totpoints) {
@@ -658,6 +666,9 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
MEM_SAFE_FREE(tgpil->nextFrame);
MEM_SAFE_FREE(tgpil->interFrame);
+ /* Free list of strokes. */
+ BLI_freelistN(&tgpil->selected_strokes);
+
/* Free Hash tablets. */
if (tgpil->used_strokes != NULL) {
BLI_ghash_free(tgpil->used_strokes, NULL, NULL);
@@ -1292,9 +1303,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
bGPDframe *nextFrame = BKE_gpencil_frame_duplicate(gpf_next, true);
/* Create a table with source and target pair of strokes. */
+ ListBase selected_strokes = {NULL};
GHash *used_strokes = BLI_ghash_ptr_new(__func__);
GHash *pair_strokes = BLI_ghash_ptr_new(__func__);
-
LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) {
bGPDstroke *gps_to = NULL;
/* Only selected. */
@@ -1342,7 +1353,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
}
- /* Insert the pair entry in the hash table. */
+ /* Insert the pair entry in the hash table and in the list of strokes to keep same order.
+ */
+ BLI_addtail(&selected_strokes, BLI_genericNodeN(gps_from));
BLI_ghash_insert(pair_strokes, gps_from, gps_to);
}
@@ -1369,11 +1382,12 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* Apply the factor to all pair of strokes. */
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
-
+ LISTBASE_FOREACH (LinkData *, link, &selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(pair_strokes, gps_from);
/* Create new stroke. */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
new_stroke->flag |= GP_STROKE_TAG;
@@ -1394,6 +1408,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
}
+ BLI_freelistN(&selected_strokes);
+
/* Free Hash tablets. */
if (used_strokes != NULL) {
BLI_ghash_free(used_strokes, NULL, NULL);
@@ -1419,34 +1435,31 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *col, *row;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "step", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "step", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "layers", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "layers", 0, NULL, ICON_NONE);
if (CTX_data_mode_enum(C) == CTX_MODE_EDIT_GPENCIL) {
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
}
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "flip", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "flip", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "smooth_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "smooth_steps", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "type", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE);
if (type == GP_IPO_CURVEMAP) {
/* Get an RNA pointer to ToolSettings to give to the custom curve. */
@@ -1460,16 +1473,16 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
}
else if (type != GP_IPO_LINEAR) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "easing", 0, NULL, ICON_NONE);
if (type == GP_IPO_BACK) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "back", 0, NULL, ICON_NONE);
}
else if (type == GP_IPO_ELASTIC) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "amplitude", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "period", 0, NULL, ICON_NONE);
}
}
}
@@ -1690,7 +1703,7 @@ static bool gpencil_interpolate_reverse_poll(bContext *C)
if (area == NULL) {
return false;
}
- if ((area->spacetype != SPACE_VIEW3D) && (area->spacetype != SPACE_ACTION)) {
+ if (!ELEM(area->spacetype, SPACE_VIEW3D, SPACE_ACTION)) {
return false;
}
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
index b7ed77801c0..55468dffab0 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.c
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -402,25 +402,6 @@ static int gpencil_bake_mesh_animation_invoke(bContext *C,
void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot)
{
- static const EnumPropertyItem reproject_type[] = {
- {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""},
- {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
- {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
- {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
- {GP_REPROJECT_VIEW,
- "VIEW",
- 0,
- "View",
- "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
- "using 'Cursor' Stroke Placement"},
- {GP_REPROJECT_CURSOR,
- "CURSOR",
- 0,
- "Cursor",
- "Reproject the strokes using the orientation of 3D cursor"},
- {0, NULL, 0, NULL, NULL},
- };
-
static const EnumPropertyItem target_object_modes[] = {
{GP_TARGET_OB_NEW, "NEW", 0, "New Object", ""},
{GP_TARGET_OB_SELECTED, "SELECTED", 0, "Selected Object", ""},
@@ -491,5 +472,10 @@ void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot)
RNA_def_int(
ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
- RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
+ RNA_def_enum(ot->srna,
+ "project_type",
+ rna_gpencil_reproject_type_items,
+ GP_REPROJECT_VIEW,
+ "Projection Type",
+ "");
}
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 1a6cb5670c4..35640cf3b66 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -601,6 +601,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_layer_mask_add);
WM_operatortype_append(GPENCIL_OT_layer_mask_remove);
+ WM_operatortype_append(GPENCIL_OT_layer_mask_move);
WM_operatortype_append(GPENCIL_OT_hide);
WM_operatortype_append(GPENCIL_OT_reveal);
@@ -621,6 +622,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_convert);
WM_operatortype_append(GPENCIL_OT_bake_mesh_animation);
+ WM_operatortype_append(GPENCIL_OT_bake_grease_pencil_animation);
WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil);
#ifdef WITH_POTRACE
@@ -647,9 +649,11 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance);
WM_operatortype_append(GPENCIL_OT_stroke_merge_material);
WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color);
+ WM_operatortype_append(GPENCIL_OT_stroke_normalize);
WM_operatortype_append(GPENCIL_OT_material_to_vertex_color);
WM_operatortype_append(GPENCIL_OT_extract_palette_vertex);
+ WM_operatortype_append(GPENCIL_OT_materials_copy_to_object);
WM_operatortype_append(GPENCIL_OT_transform_fill);
WM_operatortype_append(GPENCIL_OT_reset_transform_fill);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 1217a3a7e8f..e40748e5f6e 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1332,7 +1332,7 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
/* only erase stroke points that are visible */
static bool gpencil_stroke_eraser_is_occluded(
- tGPsdata *p, bGPDlayer *gpl, const bGPDspoint *pt, const int x, const int y)
+ tGPsdata *p, bGPDlayer *gpl, bGPDspoint *pt, const int x, const int y)
{
Object *obact = (Object *)p->ownerPtr.data;
Brush *brush = p->brush;
@@ -1364,7 +1364,11 @@ static bool gpencil_stroke_eraser_is_occluded(
mul_v3_m4v3(fpt, diff_mat, &pt->x);
const float depth_pt = view3d_point_depth(rv3d, fpt);
+ /* Checked occlusion flag. */
+ pt->flag |= GP_SPOINT_TEMP_TAG;
if (depth_pt > depth_mval) {
+ /* Is occluded. */
+ pt->flag |= GP_SPOINT_TEMP_TAG2;
return true;
}
}
@@ -1540,6 +1544,10 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
for (i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_TAG;
+ /* Occlusion already checked. */
+ pt->flag &= ~GP_SPOINT_TEMP_TAG;
+ /* Point is occluded. */
+ pt->flag &= ~GP_SPOINT_TEMP_TAG2;
}
/* First Pass: Loop over the points in the stroke
@@ -1585,9 +1593,23 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
* - this assumes that linewidth is irrelevant
*/
if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) {
- if ((gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]) == false)) {
+
+ bool is_occluded_pt0, is_occluded_pt1, is_occluded_pt2 = true;
+ is_occluded_pt0 = (pt0 && ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0)) ?
+ ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]);
+ if (is_occluded_pt0) {
+ is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ?
+ ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]);
+ if (is_occluded_pt1) {
+ is_occluded_pt2 = ((pt2->flag & GP_SPOINT_TEMP_TAG) != 0) ?
+ ((pt2->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]);
+ }
+ }
+
+ if (!is_occluded_pt0 || !is_occluded_pt1 || !is_occluded_pt2) {
/* Point is affected: */
/* Adjust thickness
* - Influence of eraser falls off with distance from the middle of the eraser
@@ -1722,7 +1744,7 @@ static void gpencil_stroke_doeraser(tGPsdata *p)
if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph, p->region, v3d, 0);
+ ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
}
}
@@ -2120,7 +2142,7 @@ static void gpencil_paint_initstroke(tGPsdata *p,
/* get active layer (or add a new one if non-existent) */
p->gpl = BKE_gpencil_layer_active_get(p->gpd);
if (p->gpl == NULL) {
- p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true, false);
changed = true;
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
@@ -2305,8 +2327,14 @@ static void gpencil_paint_strokeend(tGPsdata *p)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(
- p->depsgraph, p->region, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* check if doing eraser or not */
@@ -3259,7 +3287,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
gpencil_guide_event_handling(C, op, event, p);
}
- if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) {
+ if ((ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) {
/* FIXME: use the mode switching operator, this misses notifiers, messages. */
/* Just set paintmode flag... */
p->gpd->flag |= GP_DATA_STROKE_PAINTMODE;
@@ -3671,14 +3699,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* is essential for ensuring that they can quickly return to that view
*/
}
- else if ((event->type == EVT_BKEY) && (event->val == KM_RELEASE)) {
- /* Add Blank Frame
- * - Since this operator is non-modal, we can just call it here, and keep going...
- * - This operator is especially useful when animating
- */
- WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL);
- estate = OPERATOR_RUNNING_MODAL;
- }
else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) {
gpencil_guide_event_handling(C, op, event, p);
estate = OPERATOR_RUNNING_MODAL;
@@ -3691,7 +3711,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Exit painting mode (and/or end current stroke).
*
*/
- if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) {
+ if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY)) {
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index dfff0ce639e..5f02bbf0a77 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -314,7 +314,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* if layer doesn't exist, create a new one */
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true);
+ gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true, false);
}
tgpi->gpl = gpl;
@@ -785,10 +785,14 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(tgpi->win, tgpi->region);
- ED_view3d_autodist_init(tgpi->depsgraph,
- tgpi->region,
- tgpi->v3d,
- (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(tgpi->depsgraph,
+ tgpi->region,
+ tgpi->v3d,
+ NULL,
+ (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points");
tGPspoint *ptc = &points2D[0];
@@ -1532,24 +1536,22 @@ static void gpencil_primitive_strength(tGPDprimitive *tgpi, bool reset)
Brush *brush = tgpi->brush;
BrushGpencilSettings *brush_settings = brush->gpencil_settings;
- if (brush) {
- if (reset) {
- brush_settings->draw_strength = tgpi->brush_strength;
- tgpi->brush_strength = 0.0f;
- }
- else {
- if (tgpi->brush_strength == 0.0f) {
- tgpi->brush_strength = brush_settings->draw_strength;
- }
- float move[2];
- sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
- float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f;
- brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move));
+ if (reset) {
+ brush_settings->draw_strength = tgpi->brush_strength;
+ tgpi->brush_strength = 0.0f;
+ }
+ else {
+ if (tgpi->brush_strength == 0.0f) {
+ tgpi->brush_strength = brush_settings->draw_strength;
}
-
- /* limit low limit because below 0.2f the stroke is invisible */
- CLAMP(brush_settings->draw_strength, 0.2f, 1.0f);
+ float move[2];
+ sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
+ float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f;
+ brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move));
}
+
+ /* limit low limit because below 0.2f the stroke is invisible */
+ CLAMP(brush_settings->draw_strength, 0.2f, 1.0f);
}
/* brush size */
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index aab08e9c8c4..e1776988186 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -505,7 +505,7 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna,
"unselect_ends",
- true,
+ false,
"Unselect Ends",
"Do not select the first and last point of the stroke");
}
diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c
index d2e5fa3db32..cd75f9313ad 100644
--- a/source/blender/editors/gpencil/gpencil_trace_ops.c
+++ b/source/blender/editors/gpencil/gpencil_trace_ops.c
@@ -207,7 +207,7 @@ static void trace_initialize_job_data(TraceJob *trace_job)
trace_job->gpd = (bGPdata *)trace_job->ob_gpencil->data;
trace_job->gpl = BKE_gpencil_layer_active_get(trace_job->gpd);
if (trace_job->gpl == NULL) {
- trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true);
+ trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true, false);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c
index ada777d43f3..12c38fb2744 100644
--- a/source/blender/editors/gpencil/gpencil_trace_utils.c
+++ b/source/blender/editors/gpencil/gpencil_trace_utils.c
@@ -281,7 +281,6 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
mat_mask_idx = ob->totcol - 1;
}
- potrace_path_t *path = st->plist;
int n, *tag;
potrace_dpoint_t(*c)[3];
@@ -289,7 +288,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
* good results using the Potrace data. */
const float scalef = 0.008f * scale;
/* Draw each curve. */
- path = st->plist;
+ potrace_path_t *path = st->plist;
while (path != NULL) {
n = path->curve.n;
tag = path->curve.tag;
@@ -308,9 +307,16 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
if (gps->totpoints == 0) {
add_point(gps, scalef, offset, c[n - 1][2].x, c[n - 1][2].y);
}
+ else {
+ add_point(gps, scalef, offset, last[0], last[1]);
+ }
+
add_point(gps, scalef, offset, c[i][1].x, c[i][1].y);
add_point(gps, scalef, offset, c[i][2].x, c[i][2].y);
+
+ last[0] = c[i][2].x;
+ last[1] = c[i][2].y;
break;
}
case POTRACE_CURVETO: {
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 574670de7ca..c9ef340b9d3 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -543,6 +543,38 @@ bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0,
}
/* ******************************************************** */
+/* Selection Validity Testing */
+
+bool ED_gpencil_frame_has_selected_stroke(const bGPDframe *gpf)
+{
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ED_gpencil_layer_has_selected_stroke(const bGPDlayer *gpl, const bool is_multiedit)
+{
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (ED_gpencil_frame_has_selected_stroke(gpf)) {
+ return true;
+ }
+ }
+ /* If not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+
+ return false;
+}
+
+/* ******************************************************** */
/* Stroke Validity Testing */
/* Check whether given stroke can be edited given the supplied context */
@@ -648,7 +680,7 @@ void gpencil_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
view3d_operator_needs_opengl(C);
view3d_region_operator_needs_opengl(win, region);
- ED_view3d_autodist_init(depsgraph, region, v3d, 0);
+ ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
/* for camera view set the subrect */
if (rv3d->persp == RV3D_CAMOB) {
@@ -1253,7 +1285,11 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
float location[3] = {0.0f, 0.0f, 0.0f};
float normal[3] = {0.0f, 0.0f, 0.0f};
- ED_view3d_win_to_ray(region, xy, &ray_start[0], &ray_normal[0]);
+ BLI_assert(gps->flag & GP_STROKE_3DSPACE);
+ BLI_assert(gsc->area && gsc->area->spacetype == SPACE_VIEW3D);
+ const View3D *v3d = gsc->area->spacedata.first;
+ ED_view3d_win_to_ray_clipped(
+ depsgraph, region, v3d, xy, &ray_start[0], &ray_normal[0], true);
if (ED_transform_snap_object_project_ray(sctx,
depsgraph,
&(const struct SnapObjectParams){
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 0c4576096fb..85563b76f38 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -125,6 +125,13 @@ bool ED_armature_edit_deselect_all_visible(struct Object *obedit);
bool ED_armature_edit_deselect_all_multi_ex(struct Base **bases, uint bases_len);
bool ED_armature_edit_deselect_all_visible_multi_ex(struct Base **bases, uint bases_len);
bool ED_armature_edit_deselect_all_visible_multi(struct bContext *C);
+bool ED_armature_edit_select_pick_bone(struct bContext *C,
+ struct Base *basact,
+ struct EditBone *ebone,
+ int selmask,
+ bool extend,
+ bool deselect,
+ bool toggle);
bool ED_armature_edit_select_pick(
struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
bool ED_armature_edit_select_op_from_tagged(struct bArmature *arm, const int sel_op);
@@ -201,6 +208,13 @@ void ED_pose_recalculate_paths(struct bContext *C,
ePosePathCalcRange range);
/* pose_select.c */
+void ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer,
+ struct View3D *v3d,
+ struct Object *ob,
+ struct Bone *bone,
+ bool extend,
+ bool deselect,
+ bool toggle);
bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer,
struct View3D *v3d,
struct Base *base,
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index 983ae94b637..8118e3c6c69 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -110,7 +110,7 @@ struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceF
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 int temp_win_size[2],
const bool is_maximized);
void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region);
diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h
index dfc8cfea5ce..571519e52f7 100644
--- a/source/blender/editors/include/ED_gizmo_library.h
+++ b/source/blender/editors/include/ED_gizmo_library.h
@@ -261,9 +261,20 @@ struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *sce
const struct View3D *v3d,
struct wmGizmo *gz);
+typedef enum {
+ ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE = 1 << 0,
+ ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE = 1 << 1,
+ ED_SNAPGIZMO_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */
+ ED_SNAPGIZMO_SNAP_ONLY_ACTIVE = 1 << 3,
+ ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL = 1 << 4,
+ ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE = 1 << 5,
+} eSnapGizmo;
+
+void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag);
+void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, eSnapGizmo flag);
+bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, eSnapGizmo flag);
+
bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz);
-void ED_gizmotypes_snap_3d_toggle_set(struct wmGizmo *gz, bool enable);
-void ED_gizmotypes_snap_3d_toggle_clear(struct wmGizmo *gz);
bool ED_gizmotypes_snap_3d_is_enabled(struct wmGizmo *gz);
short ED_gizmotypes_snap_3d_update(struct wmGizmo *gz,
@@ -271,9 +282,9 @@ short ED_gizmotypes_snap_3d_update(struct wmGizmo *gz,
const struct ARegion *region,
const struct View3D *v3d,
const struct wmWindowManager *wm,
- const float mval_fl[2],
- float r_loc[3],
- float r_nor[3]);
+ const float mval_fl[2]);
+void ED_gizmotypes_snap_3d_data_get(
+ struct wmGizmo *gz, float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index e9ac21f60cf..bad080e1609 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -144,6 +144,8 @@ bool ED_gpencil_data_owner_is_annotation(struct PointerRNA *owner_ptr);
bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfra);
/* ----------- Stroke Editing Utilities ---------------- */
+bool ED_gpencil_frame_has_selected_stroke(const struct bGPDframe *gpf);
+bool ED_gpencil_layer_has_selected_stroke(const struct bGPDlayer *gpl, const bool is_multiedit);
bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *area, const struct bGPDstroke *gps);
bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps);
@@ -249,6 +251,7 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y);
/* ----------- Add Primitive Utilities -------------- */
+void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_lineart(struct bContext *C, struct Object *ob);
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 12d6f1fce54..179c9d5b30d 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -511,6 +511,7 @@ bool ED_autokeyframe_property(struct bContext *C,
#define ANIM_KS_ROTATION_ID "Rotation"
#define ANIM_KS_SCALING_ID "Scaling"
#define ANIM_KS_LOC_ROT_SCALE_ID "LocRotScale"
+#define ANIM_KS_LOC_ROT_SCALE_CPROP_ID "LocRotScaleCProp"
#define ANIM_KS_AVAILABLE_ID "Available"
#define ANIM_KS_WHOLE_CHARACTER_ID "WholeCharacter"
#define ANIM_KS_WHOLE_CHARACTER_SELECTED_ID "WholeCharacterSelected"
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 85e7a491feb..b8e9f6e8871 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -164,16 +164,16 @@ void EDBM_select_mirrored(struct BMEditMesh *em,
int *r_totfail);
struct BMVert *EDBM_vert_find_nearest_ex(struct ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan_p,
const bool use_select_bias,
bool use_cycle,
struct Base **bases,
uint bases_len,
uint *r_base_index);
-struct BMVert *EDBM_vert_find_nearest(struct ViewContext *vc, float *r_dist);
+struct BMVert *EDBM_vert_find_nearest(struct ViewContext *vc, float *dist_px_manhattan_p);
struct BMEdge *EDBM_edge_find_nearest_ex(struct ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan,
float *r_dist_center,
const bool use_select_bias,
bool use_cycle,
@@ -181,18 +181,19 @@ struct BMEdge *EDBM_edge_find_nearest_ex(struct ViewContext *vc,
struct Base **bases,
uint bases_len,
uint *r_base_index);
-struct BMEdge *EDBM_edge_find_nearest(struct ViewContext *vc, float *r_dist);
+struct BMEdge *EDBM_edge_find_nearest(struct ViewContext *vc, float *dist_px_manhattan_p);
struct BMFace *EDBM_face_find_nearest_ex(struct ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan,
float *r_dist_center,
+ const bool use_zbuf_single_px,
const bool use_select_bias,
bool use_cycle,
struct BMFace **r_efa_zbuf,
struct Base **bases,
uint bases_len,
uint *r_base_index);
-struct BMFace *EDBM_face_find_nearest(struct ViewContext *vc, float *r_dist);
+struct BMFace *EDBM_face_find_nearest(struct ViewContext *vc, float *dist_px_manhattan_p);
bool EDBM_unified_findnearest(struct ViewContext *vc,
struct Base **bases,
diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h
index 50f1ce1efe2..d5685788ce1 100644
--- a/source/blender/editors/include/ED_numinput.h
+++ b/source/blender/editors/include/ED_numinput.h
@@ -107,8 +107,9 @@ bool user_string_to_number(bContext *C,
const char *str,
const struct UnitSettings *unit,
int type,
- const char *error_prefix,
- double *r_value);
+ double *r_value,
+ const bool use_single_line_error,
+ char **r_error);
/** \} */
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 0767ce21382..1738c383328 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -178,6 +178,9 @@ void ED_object_base_active_refresh(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer);
void ED_object_base_free_and_unlink(struct Main *bmain, struct Scene *scene, struct Object *ob);
+void ED_object_base_free_and_unlink_no_indirect_check(struct Main *bmain,
+ struct Scene *scene,
+ struct Object *ob);
bool ED_object_base_deselect_all_ex(struct ViewLayer *view_layer,
struct View3D *v3d,
int action,
@@ -207,6 +210,8 @@ bool ED_object_editmode_exit_ex(struct Main *bmain,
int flag);
bool ED_object_editmode_exit(struct bContext *C, int flag);
+bool ED_object_editmode_free_ex(struct Main *bmain, struct Object *obedit);
+
bool ED_object_editmode_exit_multi_ex(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
@@ -242,6 +247,7 @@ void ED_object_texture_paint_mode_enter(struct bContext *C);
void ED_object_texture_paint_mode_exit_ex(struct Main *bmain, struct Scene *scene, Object *ob);
void ED_object_texture_paint_mode_exit(struct bContext *C);
+bool ED_object_particle_edit_mode_supported(const Object *ob);
void ED_object_particle_edit_mode_enter_ex(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *ob);
@@ -289,12 +295,12 @@ void ED_object_add_mesh_props(struct wmOperatorType *ot);
bool ED_object_add_generic_get_opts(struct bContext *C,
struct wmOperator *op,
const char view_align_axis,
- float loc[3],
- float rot[3],
- float scale[3],
- bool *enter_editmode,
- unsigned short *local_view_bits,
- bool *is_view_aligned);
+ float r_loc[3],
+ float r_rot[3],
+ float r_scale[3],
+ bool *r_enter_editmode,
+ unsigned short *r_local_view_bits,
+ bool *r_is_view_aligned);
struct Object *ED_object_add_type_with_obdata(struct bContext *C,
const int type,
diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h
index ed35b9138f3..0fb06639dbf 100644
--- a/source/blender/editors/include/ED_render.h
+++ b/source/blender/editors/include/ED_render.h
@@ -91,8 +91,7 @@ void ED_preview_shader_job(const struct bContext *C,
int sizex,
int sizey,
int method);
-void ED_preview_icon_render(struct Main *bmain,
- struct Depsgraph *depsgraph,
+void ED_preview_icon_render(const struct bContext *C,
struct Scene *scene,
struct ID *id,
unsigned int *rect,
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index b3205acb8ee..bdd7ec571dc 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -162,7 +162,7 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area);
void ED_area_tag_redraw_regiontype(ScrArea *area, int type);
void ED_area_tag_refresh(ScrArea *area);
void ED_area_do_refresh(struct bContext *C, ScrArea *area);
-struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[]);
+struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[2]);
void ED_area_status_text(ScrArea *area, const char *str);
void ED_area_newspace(struct bContext *C, ScrArea *area, int type, const bool skip_region_exit);
void ED_area_prevspace(struct bContext *C, ScrArea *area);
@@ -200,8 +200,6 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area);
/* screens */
void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm);
void ED_screen_draw_edges(struct wmWindow *win);
-void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2);
-void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac);
void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win);
void ED_screen_ensure_updated(struct wmWindowManager *wm,
struct wmWindow *win,
@@ -304,6 +302,7 @@ void ED_operatortypes_workspace(void);
/* operators; context poll callbacks */
bool ED_operator_screenactive(struct bContext *C);
+bool ED_operator_screenactive_nobackground(struct bContext *C);
bool ED_operator_screen_mainwinactive(struct bContext *C);
bool ED_operator_areaactive(struct bContext *C);
bool ED_operator_regionactive(struct bContext *C);
@@ -449,10 +448,10 @@ enum {
};
/* SCREEN_OT_space_context_cycle direction */
-enum {
+typedef enum eScreenCycle {
SPACE_CONTEXT_CYCLE_PREV,
SPACE_CONTEXT_CYCLE_NEXT,
-};
+} eScreenCycle;
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_sequencer.h b/source/blender/editors/include/ED_sequencer.h
index 11eff2d583b..ae76f0b6eaf 100644
--- a/source/blender/editors/include/ED_sequencer.h
+++ b/source/blender/editors/include/ED_sequencer.h
@@ -42,6 +42,7 @@ bool ED_space_sequencer_maskedit_poll(struct bContext *C);
bool ED_space_sequencer_check_show_imbuf(struct SpaceSeq *sseq);
bool ED_space_sequencer_check_show_strip(struct SpaceSeq *sseq);
+bool ED_space_sequencer_has_visible_animation_on_strip(const struct Scene *scene);
void ED_operatormacros_sequencer(void);
diff --git a/source/blender/editors/include/ED_spreadsheet.h b/source/blender/editors/include/ED_spreadsheet.h
new file mode 100644
index 00000000000..3a07b1b9d4b
--- /dev/null
+++ b/source/blender/editors/include/ED_spreadsheet.h
@@ -0,0 +1,43 @@
+/*
+ * 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
+
+struct SpreadsheetContext;
+struct SpaceSpreadsheet;
+struct SpaceNode;
+struct ID;
+struct bNode;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct SpreadsheetContext *ED_spreadsheet_context_new(int type);
+void ED_spreadsheet_context_free(struct SpreadsheetContext *context);
+void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet);
+void ED_spreadsheet_context_path_update_tag(struct SpaceSpreadsheet *sspreadsheet);
+uint64_t ED_spreadsheet_context_path_hash(struct SpaceSpreadsheet *sspreadsheet);
+
+struct ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet);
+
+void ED_spreadsheet_set_geometry_node_context(struct SpaceSpreadsheet *sspreadsheet,
+ struct SpaceNode *snode,
+ struct bNode *node);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/include/ED_text.h b/source/blender/editors/include/ED_text.h
index 6742561735e..2284c82b3d5 100644
--- a/source/blender/editors/include/ED_text.h
+++ b/source/blender/editors/include/ED_text.h
@@ -34,6 +34,8 @@ struct UndoStep;
struct UndoType;
struct bContext;
+void ED_text_scroll_to_cursor(struct SpaceText *st, struct ARegion *region, bool center);
+
bool ED_text_region_location_from_cursor(struct SpaceText *st,
struct ARegion *region,
const int cursor_co[2],
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index 8f1be847e2b..cb6fb0dba60 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -151,8 +151,7 @@ short ED_transform_calc_orientation_from_type_ex(const struct bContext *C,
struct RegionView3D *rv3d,
struct Object *ob,
struct Object *obedit,
- const short orientation_type,
- int orientation_index_custom,
+ const short orientation_index,
const int pivot_point);
/* transform gizmos */
@@ -186,8 +185,7 @@ struct TransformCalcParams {
uint use_only_center : 1;
uint use_local_axis : 1;
/* Use 'Scene.orientation_type' when zero, otherwise subtract one and use. */
- ushort orientation_type;
- ushort orientation_index_custom;
+ ushort orientation_index;
};
int ED_transform_calc_gizmo_stats(const struct bContext *C,
const struct TransformCalcParams *params,
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 b7174964ef6..42e73bbf744 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -39,12 +39,19 @@ struct View3D;
/* ED_transform_snap_object_*** API */
-typedef enum eSnapSelect {
+typedef enum {
SNAP_ALL = 0,
SNAP_NOT_SELECTED = 1,
SNAP_NOT_ACTIVE = 2,
+ SNAP_ONLY_ACTIVE = 3,
} eSnapSelect;
+typedef enum {
+ SNAP_GEOM_FINAL = 0,
+ SNAP_GEOM_CAGE = 1,
+ SNAP_GEOM_EDIT = 2, /* Bmesh for mesh-type. */
+} eSnapEditType;
+
/** used for storing multiple hits */
struct SnapObjectHitDepth {
struct SnapObjectHitDepth *next, *prev;
@@ -54,7 +61,7 @@ struct SnapObjectHitDepth {
float no[3];
int index;
- struct Object *ob;
+ struct Object *ob_eval;
float obmat[4][4];
/* needed to tell which ray-cast this was part of,
@@ -64,10 +71,10 @@ struct SnapObjectHitDepth {
/** parameters that define which objects will be used to snap. */
struct SnapObjectParams {
- /* special context sensitive handling for the active or selected object */
+ /* Special context sensitive handling for the active or selected object. */
char snap_select;
- /* use editmode cage */
- unsigned int use_object_edit_cage : 1;
+ /* Geometry for snapping in edit mode. */
+ char edit_mode_type;
/* snap to the closest element, use when using more than one snap type */
unsigned int use_occlusion_test : 1;
/* exclude back facing geometry from snapping */
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 4de97411059..ea3d921f2c5 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -42,6 +42,7 @@ struct SpaceImage;
struct ToolSettings;
struct ViewLayer;
struct bNode;
+struct bNodeTree;
struct wmKeyConfig;
/* uvedit_ops.c */
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 3b8e062ffec..52d69d12253 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -144,15 +144,28 @@ bool ED_view3d_camera_to_view_selected(struct Main *bmain,
void ED_view3d_lastview_store(struct RegionView3D *rv3d);
/* Depth buffer */
-void ED_view3d_depth_update(struct ARegion *region);
-float ED_view3d_depth_read_cached(const struct ViewContext *vc, const int mval[2]);
+typedef enum {
+ V3D_DEPTH_NO_GPENCIL = 0,
+ V3D_DEPTH_GPENCIL_ONLY,
+ V3D_DEPTH_OBJECT_ONLY,
+} eV3DDepthOverrideMode;
+void ED_view3d_depth_override(struct Depsgraph *depsgraph,
+ struct ARegion *region,
+ struct View3D *v3d,
+ struct Object *obact,
+ eV3DDepthOverrideMode mode,
+ bool update_cache);
+bool ED_view3d_depth_read_cached(const ViewDepths *vd,
+ const int mval[2],
+ int margin,
+ float *r_depth);
bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
const int mval[2],
float r_normal[3]);
-bool ED_view3d_depth_unproject(const struct ARegion *region,
- const int mval[2],
- const double depth,
- float r_location_world[3]);
+bool ED_view3d_depth_unproject_v3(const struct ARegion *region,
+ const int mval[2],
+ const double depth,
+ float r_location_world[3]);
void ED_view3d_depth_tag_update(struct RegionView3D *rv3d);
/* Projection */
@@ -397,8 +410,13 @@ void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d,
const float obmat[4][4],
float r_pmat[4][4]);
-void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]);
-bool ED_view3d_unproject(
+void ED_view3d_project_v3(const struct ARegion *region,
+ const float world[3],
+ float r_region_co[3]);
+void ED_view3d_project_v2(const struct ARegion *region,
+ const float world[3],
+ float r_region_co[2]);
+bool ED_view3d_unproject_v3(
const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]);
/* end */
@@ -441,7 +459,7 @@ bool ED_view3d_calc_render_border(const struct Scene *scene,
struct ARegion *region,
struct rcti *rect);
-void ED_view3d_clipping_calc_from_boundbox(float clip[6][4],
+void ED_view3d_clipping_calc_from_boundbox(float clip[4][4],
const struct BoundBox *clipbb,
const bool is_flip);
void ED_view3d_clipping_calc(struct BoundBox *bb,
@@ -481,11 +499,6 @@ bool ED_view3d_autodist(struct Depsgraph *depsgraph,
const bool alphaoverride,
const float fallback_depth_pt[3]);
-/* Only draw so #ED_view3d_autodist_simple can be called many times after. */
-void ED_view3d_autodist_init(struct Depsgraph *depsgraph,
- struct ARegion *region,
- struct View3D *v3d,
- int mode);
bool ED_view3d_autodist_simple(struct ARegion *region,
const int mval[2],
float mouse_worldloc[3],
@@ -682,7 +695,7 @@ float ED_view3d_grid_scale(const struct Scene *scene,
void ED_view3d_grid_steps(const struct Scene *scene,
struct View3D *v3d,
struct RegionView3D *rv3d,
- float *r_grid_steps);
+ float r_grid_steps[8]);
float ED_view3d_grid_view_scale(struct Scene *scene,
struct View3D *v3d,
struct ARegion *region,
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index dfe0898a85b..1d335c500ca 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -186,17 +186,17 @@ enum {
UI_RETURN_POPUP_OK = 1 << 5,
};
-/* but->flag - general state flags. */
+/** #uiBut.flag general state flags. */
enum {
- /** Warning, the first 6 flags are internal. */
- UI_BUT_ICON_SUBMENU = 1 << 6,
- UI_BUT_ICON_PREVIEW = 1 << 7,
+ /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */
+ UI_BUT_ICON_SUBMENU = 1 << 7,
+ UI_BUT_ICON_PREVIEW = 1 << 8,
- UI_BUT_NODE_LINK = 1 << 8,
- UI_BUT_NODE_ACTIVE = 1 << 9,
- UI_BUT_DRAG_LOCK = 1 << 10,
+ UI_BUT_NODE_LINK = 1 << 9,
+ UI_BUT_NODE_ACTIVE = 1 << 10,
+ UI_BUT_DRAG_LOCK = 1 << 11,
/** Grayed out and un-editable. */
- UI_BUT_DISABLED = 1 << 11,
+ UI_BUT_DISABLED = 1 << 12,
UI_BUT_ANIMATED = 1 << 13,
UI_BUT_ANIMATED_KEY = 1 << 14,
@@ -723,6 +723,7 @@ void UI_but_drag_set_asset(uiBut *but,
const char *name,
const char *path,
int id_type,
+ int import_type, /* eFileAssetImportType */
int icon,
struct ImBuf *imb,
float scale);
@@ -1600,6 +1601,7 @@ void UI_but_func_search_set(uiBut *but,
uiButSearchCreateFn search_create_fn,
uiButSearchUpdateFn search_update_fn,
void *arg,
+ const bool free_arg,
uiButSearchArgFreeFn search_arg_free_fn,
uiButHandleFunc search_exec_fn,
void *active);
@@ -1728,7 +1730,7 @@ struct Panel *UI_panel_add_instanced(const struct bContext *C,
struct PointerRNA *custom_data);
void UI_panels_free_instanced(const struct bContext *C, struct ARegion *region);
-#define INSTANCED_PANEL_UNIQUE_STR_LEN 4
+#define INSTANCED_PANEL_UNIQUE_STR_LEN 16
void UI_list_panel_unique_str(struct Panel *panel, char *r_name);
typedef void (*uiListPanelIDFromDataFunc)(void *data_link, char *r_idname);
@@ -2136,7 +2138,7 @@ void uiTemplateComponentMenu(uiLayout *layout,
struct PointerRNA *ptr,
const char *propname,
const char *name);
-void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color);
+void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float color[4]);
void uiTemplateCacheFile(uiLayout *layout,
const struct bContext *C,
struct PointerRNA *ptr,
@@ -2404,9 +2406,12 @@ void uiItemS_ex(uiLayout *layout, float factor);
void uiItemSpacer(uiLayout *layout);
void uiItemPopoverPanel_ptr(
- uiLayout *layout, struct bContext *C, struct PanelType *pt, const char *name, int icon);
-void uiItemPopoverPanel(
- uiLayout *layout, struct bContext *C, const char *panel_type, const char *name, int icon);
+ uiLayout *layout, const struct bContext *C, struct PanelType *pt, const char *name, int icon);
+void uiItemPopoverPanel(uiLayout *layout,
+ const struct bContext *C,
+ const char *panel_type,
+ const char *name,
+ int icon);
void uiItemPopoverPanelFromGroup(uiLayout *layout,
struct bContext *C,
int space_id,
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index 1820c2f133c..c99c7f681b3 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -451,7 +451,7 @@ int UI_ThemeMenuShadowWidth(void);
/* only for buttons in theme editor! */
const unsigned char *UI_ThemeGetColorPtr(struct bTheme *btheme, int spacetype, int colorid);
-void UI_make_axis_color(const unsigned char *src_col, unsigned char *dst_col, const char axis);
+void UI_make_axis_color(const unsigned char src_col[3], unsigned char dst_col[3], const char axis);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 279239fcc65..26ea5f8e24a 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -52,6 +52,7 @@
#include "BKE_context.h"
#include "BKE_idprop.h"
#include "BKE_main.h"
+#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_unit.h"
@@ -1038,7 +1039,6 @@ static bool ui_but_is_rna_undo(const uiBut *but)
if (ID_CHECK_UNDO(id) == false) {
return false;
}
- return true;
}
if (but->rnapoin.type && !RNA_struct_undo_check(but->rnapoin.type)) {
return false;
@@ -1817,9 +1817,9 @@ static void ui_but_validate(const uiBut *but)
#endif
/**
- * Check if the operator \a ot poll is successfull with the context given by \a but (optionally).
+ * Check if the operator \a ot poll is successful with the context given by \a but (optionally).
* \param but: The button that might store context. Can be NULL for convenience (e.g. if there is
- * no button to take context from, but we still want to poll the operator).
+ * no button to take context from, but we still want to poll the operator).
*/
bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut *but)
{
@@ -2327,6 +2327,14 @@ bool ui_but_is_float(const uiBut *but)
return false;
}
+PropertyScaleType ui_but_scale_type(const uiBut *but)
+{
+ if (but->rnaprop) {
+ return RNA_property_ui_scale(but->rnaprop);
+ }
+ return PROP_SCALE_LINEAR;
+}
+
bool ui_but_is_bool(const uiBut *but)
{
if (ELEM(but->type,
@@ -2913,7 +2921,14 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
static bool ui_number_from_string_units(
bContext *C, const char *str, const int unit_type, const UnitSettings *unit, double *r_value)
{
- return user_string_to_number(C, str, unit, unit_type, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
+ char *error = NULL;
+ const bool ok = user_string_to_number(C, str, unit, unit_type, r_value, true, &error);
+ if (error) {
+ ReportList *reports = CTX_wm_reports(C);
+ BKE_reportf(reports, RPT_ERROR, "%s: %s", UI_NUMBER_EVAL_ERROR_PREFIX, error);
+ MEM_freeN(error);
+ }
+ return ok;
}
static bool ui_number_from_string_units_with_but(bContext *C,
@@ -2930,7 +2945,11 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value)
{
bool ok;
#ifdef WITH_PYTHON
- ok = BPY_run_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
+ struct BPy_RunErrInfo err_info = {
+ .reports = CTX_wm_reports(C),
+ .report_prefix = UI_NUMBER_EVAL_ERROR_PREFIX,
+ };
+ ok = BPY_run_string_as_number(C, NULL, str, &err_info, r_value);
#else
UNUSED_VARS(C);
*r_value = atof(str);
@@ -3188,19 +3207,17 @@ void ui_but_range_set_hard(uiBut *but)
const PropertyType type = RNA_property_type(but->rnaprop);
- /* clamp button range to something reasonable in case
- * we get -inf/inf from RNA properties */
if (type == PROP_INT) {
int imin, imax;
RNA_property_int_range(&but->rnapoin, but->rnaprop, &imin, &imax);
- but->hardmin = (imin == INT_MIN) ? -1e4 : imin;
- but->hardmax = (imin == INT_MAX) ? 1e4 : imax;
+ but->hardmin = imin;
+ but->hardmax = imax;
}
else if (type == PROP_FLOAT) {
float fmin, fmax;
RNA_property_float_range(&but->rnapoin, but->rnaprop, &fmin, &fmax);
- but->hardmin = (fmin == -FLT_MAX) ? (float)-1e4 : fmin;
- but->hardmax = (fmax == FLT_MAX) ? (float)1e4 : fmax;
+ but->hardmin = fmin;
+ but->hardmax = fmax;
}
}
@@ -6127,6 +6144,7 @@ void UI_but_drag_set_asset(uiBut *but,
const char *name,
const char *path,
int id_type,
+ int import_type,
int icon,
struct ImBuf *imb,
float scale)
@@ -6136,6 +6154,7 @@ void UI_but_drag_set_asset(uiBut *but,
BLI_strncpy(asset_drag->name, name, sizeof(asset_drag->name));
asset_drag->path = path;
asset_drag->id_type = id_type;
+ asset_drag->import_type = import_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 */
@@ -6591,6 +6610,8 @@ uiBut *uiDefSearchBut(uiBlock *block,
* \param search_create_fn: Function to create the menu.
* \param search_update_fn: Function to refresh search content after the search text has changed.
* \param arg: user value.
+ * \param free_arg: Set to true if the argument is newly allocated memory for every redraw and
+ * should be freed when the button is destroyed.
* \param search_arg_free_fn: When non-null, use this function to free \a arg.
* \param search_exec_fn: Function that executes the action, gets \a arg as the first argument.
* The second argument as the active item-pointer
@@ -6601,6 +6622,7 @@ void UI_but_func_search_set(uiBut *but,
uiButSearchCreateFn search_create_fn,
uiButSearchUpdateFn search_update_fn,
void *arg,
+ const bool free_arg,
uiButSearchArgFreeFn search_arg_free_fn,
uiButHandleFunc search_exec_fn,
void *active)
@@ -6636,11 +6658,17 @@ void UI_but_func_search_set(uiBut *but,
}
#endif
/* Handling will pass the active item as arg2 later, so keep it NULL here. */
- UI_but_func_set(but, search_exec_fn, search_but->arg, NULL);
+ if (free_arg) {
+ UI_but_funcN_set(but, search_exec_fn, search_but->arg, NULL);
+ }
+ else {
+ UI_but_func_set(but, search_exec_fn, search_but->arg, NULL);
+ }
}
- /* search buttons show red-alert if item doesn't exist, not for menus */
- if (0 == (but->block->flag & UI_BLOCK_LOOP)) {
+ /* search buttons show red-alert if item doesn't exist, not for menus. Don't do this for
+ * buttons where any result is valid anyway, since any string will be valid anyway. */
+ if (0 == (but->block->flag & UI_BLOCK_LOOP) && !search_but->results_are_suggestions) {
/* skip empty buttons, not all buttons need input, we only show invalid */
if (but->drawstr[0]) {
ui_but_search_refresh(search_but);
@@ -6780,6 +6808,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
ui_searchbox_create_generic,
operator_enum_search_update_fn,
but,
+ false,
NULL,
operator_enum_search_exec_fn,
NULL);
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 91c19ff2850..b142e383df0 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -399,7 +399,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
"'%s').label",
idname);
char *expr_result = NULL;
- if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
STRNCPY(drawstr, expr_result);
MEM_freeN(expr_result);
}
@@ -542,9 +542,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
const PropertyType type = RNA_property_type(prop);
const PropertySubType subtype = RNA_property_subtype(prop);
bool is_anim = RNA_property_animateable(ptr, prop);
- const bool is_editable = RNA_property_editable(ptr, prop);
const bool is_idprop = RNA_property_is_idprop(prop);
- const bool is_set = RNA_property_is_set(ptr, prop);
/* second slower test,
* saved people finding keyframe items in menus when its not possible */
@@ -893,12 +891,6 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
"all",
1);
}
- if (is_editable /*&& is_idprop*/ && is_set) {
- uiItemO(layout,
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Unset"),
- ICON_NONE,
- "UI_OT_unset_property_button");
- }
if (is_idprop && !is_array && ELEM(type, PROP_INT, PROP_FLOAT)) {
uiItemO(layout,
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index 178f663ff58..b52bfc81b7a 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -130,14 +130,19 @@ void eyedropper_draw_cursor_text_region(const struct bContext *C,
const char *name)
{
wmWindow *win = CTX_wm_window(C);
- const int x = win->eventstate->x - region->winrct.xmin;
- const int y = win->eventstate->y - region->winrct.ymin;
+ const int x = win->eventstate->x;
+ const int y = win->eventstate->y;
if ((name[0] == '\0') || (BLI_rcti_isect_pt(&region->winrct, x, y) == false)) {
return;
}
- eyedropper_draw_cursor_text_ex(x, y, name);
+ const int mval[2] = {
+ x - region->winrct.xmin,
+ y - region->winrct.ymin,
+ };
+
+ eyedropper_draw_cursor_text_ex(mval[0], mval[1], name);
}
/**
diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c
index 4ae6f66281f..ba72cecc514 100644
--- a/source/blender/editors/interface/interface_eyedropper_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_color.c
@@ -116,14 +116,12 @@ static bool eyedropper_init(bContext *C, wmOperator *op)
float col[4];
RNA_property_float_get_array(&eye->ptr, eye->prop, col);
- if (ELEM(eye->ptr.type, &RNA_CompositorNodeCryptomatteV2, &RNA_CompositorNodeCryptomatte)) {
+ if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) {
eye->crypto_node = (bNode *)eye->ptr.data;
- eye->cryptomatte_session = ntreeCompositCryptomatteSession(eye->crypto_node);
+ eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C),
+ eye->crypto_node);
eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye);
}
- else {
- eye->crypto_node = NULL;
- }
if (prop_subtype != PROP_COLOR) {
Scene *scene = CTX_data_scene(C);
@@ -202,6 +200,57 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay
return false;
}
+static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node,
+ const char *prefix,
+ const float fpos[2],
+ float r_col[3])
+{
+ bool success = false;
+ Scene *scene = (Scene *)node->id;
+ BLI_assert(GS(scene->id.name) == ID_SCE);
+ Render *re = RE_GetSceneRender(scene);
+
+ if (re) {
+ RenderResult *rr = RE_AcquireResultRead(re);
+ if (rr) {
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name);
+ success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col);
+ if (success) {
+ break;
+ }
+ }
+ }
+ RE_ReleaseResult(re);
+ }
+ return success;
+}
+
+static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node,
+ NodeCryptomatte *crypto,
+ const char *prefix,
+ const float fpos[2],
+ float r_col[3])
+{
+ bool success = false;
+ Image *image = (Image *)node->id;
+ BLI_assert((image == NULL) || (GS(image->id.name) == ID_IM));
+ ImageUser *iuser = &crypto->iuser;
+
+ if (image && image->type == IMA_TYPE_MULTILAYER) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL);
+ if (image->rr) {
+ LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) {
+ success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col);
+ if (success) {
+ break;
+ }
+ }
+ }
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ return success;
+}
static bool eyedropper_cryptomatte_sample_fl(
bContext *C, Eyedropper *eye, int mx, int my, float r_col[3])
@@ -258,53 +307,19 @@ static bool eyedropper_cryptomatte_sample_fl(
return false;
}
- bool success = false;
/* TODO(jbakker): Migrate this file to cc and use std::string as return param. */
char prefix[MAX_NAME + 1];
- ntreeCompositCryptomatteLayerPrefix(node, prefix, sizeof(prefix) - 1);
+ const Scene *scene = CTX_data_scene(C);
+ ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1);
prefix[MAX_NAME] = '\0';
if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) {
- Scene *scene = (Scene *)node->id;
- BLI_assert(GS(scene->id.name) == ID_SCE);
- Render *re = RE_GetSceneRender(scene);
-
- if (re) {
- RenderResult *rr = RE_AcquireResultRead(re);
- if (rr) {
- LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name);
- success = eyedropper_cryptomatte_sample_renderlayer_fl(
- render_layer, prefix, fpos, r_col);
- if (success) {
- break;
- }
- }
- }
- RE_ReleaseResult(re);
- }
+ return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col);
}
- else if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) {
- Image *image = (Image *)node->id;
- BLI_assert(GS(image->id.name) == ID_IM);
- ImageUser *iuser = &crypto->iuser;
-
- if (image && image->type == IMA_TYPE_MULTILAYER) {
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL);
- if (image->rr) {
- LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) {
- success = eyedropper_cryptomatte_sample_renderlayer_fl(
- render_layer, prefix, fpos, r_col);
- if (success) {
- break;
- }
- }
- }
- BKE_image_release_ibuf(image, ibuf, NULL);
- }
+ if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) {
+ return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col);
}
-
- return success;
+ return false;
}
/**
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index a5a5a69728e..5f98a501bec 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -126,6 +126,24 @@
#define UI_MAX_PASSWORD_STR 128
/**
+ * This is a lower limit on the soft minimum of the range.
+ * Usually the derived lower limit from the visible precision is higher,
+ * so this number is the backup minimum.
+ *
+ * Logarithmic scale does not work with a minimum value of zero,
+ * but we want to support it anyway. It is set to 0.5e... for
+ * correct rounding since when the tweaked value is lower than
+ * the log minimum (lower limit), it will snap to 0.
+ */
+#define UI_PROP_SCALE_LOG_MIN 0.5e-8f
+/**
+ * This constant defines an offset for the precision change in
+ * snap rounding, when going to higher values. It is set to
+ * `0.5 - log10(3) = 0.03` to make the switch at `0.3` values.
+ */
+#define UI_PROP_SCALE_LOG_SNAP_OFFSET 0.03f
+
+/**
* When #USER_CONTINUOUS_MOUSE is disabled or tablet input is used,
* Use this as a maximum soft range for mapping cursor motion to the value.
* Otherwise min/max of #FLT_MAX, #INT_MAX cause small adjustments to jump to large numbers.
@@ -477,6 +495,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C,
static void ui_do_but_extra_operator_icons_mousemove(uiBut *but,
uiHandleButtonData *data,
const wmEvent *event);
+static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data);
#ifdef USE_DRAG_MULTINUM
static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block);
@@ -1114,6 +1133,13 @@ static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
{
if (data->str) {
+ double value;
+ /* Check if the string value is a number and cancel if it's equal to the startvalue. */
+ if (ui_but_string_eval_number(C, but, data->str, &value) && (value == data->startvalue)) {
+ data->cancel = true;
+ return;
+ }
+
if (ui_but_string_set(C, but, data->str)) {
data->value = ui_but_value_get(but);
}
@@ -2320,16 +2346,16 @@ static int get_but_property_array_length(uiBut *but)
}
static void ui_but_set_float_array(
- bContext *C, uiBut *but, uiHandleButtonData *data, float *values, int array_length)
+ bContext *C, uiBut *but, uiHandleButtonData *data, const float *values, const int values_len)
{
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
- for (int i = 0; i < array_length; i++) {
+ for (int i = 0; i < values_len; i++) {
RNA_property_float_set_index(&but->rnapoin, but->rnaprop, i, values[i]);
}
if (data) {
if (but->type == UI_BTYPE_UNITVEC) {
- BLI_assert(array_length == 3);
+ BLI_assert(values_len == 3);
copy_v3_v3(data->vec, values);
}
else {
@@ -2340,56 +2366,39 @@ static void ui_but_set_float_array(
button_activate_state(C, but, BUTTON_STATE_EXIT);
}
-static void float_array_to_string(float *values,
- int array_length,
+static void float_array_to_string(const float *values,
+ const int values_len,
char *output,
int output_len_max)
{
- /* to avoid buffer overflow attacks; numbers are quite arbitrary */
- BLI_assert(output_len_max > 15);
- output_len_max -= 10;
-
- int current_index = 0;
- output[current_index] = '[';
- current_index++;
-
- for (int i = 0; i < array_length; i++) {
- int length = BLI_snprintf(
- output + current_index, output_len_max - current_index, "%f", values[i]);
- current_index += length;
-
- if (i < array_length - 1) {
- if (current_index < output_len_max) {
- output[current_index + 0] = ',';
- output[current_index + 1] = ' ';
- current_index += 2;
- }
- }
+ const int values_end = values_len - 1;
+ int ofs = 0;
+ output[ofs++] = '[';
+ for (int i = 0; i < values_len; i++) {
+ ofs += BLI_snprintf_rlen(
+ output + ofs, output_len_max - ofs, (i != values_end) ? "%f, " : "%f]", values[i]);
}
-
- output[current_index + 0] = ']';
- output[current_index + 1] = '\0';
}
static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max)
{
- const int array_length = get_but_property_array_length(but);
- float *values = alloca(array_length * sizeof(float));
+ const int values_len = get_but_property_array_length(but);
+ float *values = alloca(values_len * sizeof(float));
RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values);
- float_array_to_string(values, array_length, output, output_len_max);
+ float_array_to_string(values, values_len, output, output_len_max);
}
-static bool parse_float_array(char *text, float *values, int expected_length)
+static bool parse_float_array(char *text, float *values, int values_len_expected)
{
/* can parse max 4 floats for now */
- BLI_assert(0 <= expected_length && expected_length <= 4);
+ BLI_assert(0 <= values_len_expected && values_len_expected <= 4);
float v[5];
- const int actual_length = sscanf(
+ const int values_len_actual = sscanf(
text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]);
- if (actual_length == expected_length) {
- memcpy(values, v, sizeof(float) * expected_length);
+ if (values_len_actual == values_len_expected) {
+ memcpy(values, v, sizeof(float) * values_len_expected);
return true;
}
return false;
@@ -2400,16 +2409,16 @@ static void ui_but_paste_numeric_array(bContext *C,
uiHandleButtonData *data,
char *buf_paste)
{
- const int array_length = get_but_property_array_length(but);
- if (array_length > 4) {
+ const int values_len = get_but_property_array_length(but);
+ if (values_len > 4) {
/* not supported for now */
return;
}
- float *values = alloca(sizeof(float) * array_length);
+ float *values = alloca(sizeof(float) * values_len);
- if (parse_float_array(buf_paste, values, array_length)) {
- ui_but_set_float_array(C, but, data, values, array_length);
+ if (parse_float_array(buf_paste, values, values_len)) {
+ ui_but_set_float_array(C, but, data, values, values_len);
}
else {
WM_report(RPT_ERROR, "Expected an array of numbers: [n, n, ...]");
@@ -3354,6 +3363,8 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
if (is_num_but) {
BLI_assert(data->is_str_dynamic == false);
ui_but_convert_to_unit_alt_name(but, data->str, data->maxlen);
+
+ ui_numedit_begin_set_values(but, data);
}
/* won't change from now on */
@@ -3890,6 +3901,14 @@ static void ui_do_but_textedit_select(
/** \name Button Number Editing (various types)
* \{ */
+static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data)
+{
+ data->startvalue = ui_but_value_get(but);
+ data->origvalue = data->startvalue;
+ data->value = data->origvalue;
+ but->editval = &data->value;
+}
+
static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
{
if (but->type == UI_BTYPE_CURVE) {
@@ -3915,19 +3934,21 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
but->editvec = data->vec;
}
else {
- float softrange, softmin, softmax;
+ ui_numedit_begin_set_values(but, data);
- data->startvalue = ui_but_value_get(but);
- data->origvalue = data->startvalue;
- data->value = data->origvalue;
- but->editval = &data->value;
+ float softmin = but->softmin;
+ float softmax = but->softmax;
+ float softrange = softmax - softmin;
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
- softmin = but->softmin;
- softmax = but->softmax;
- softrange = softmax - softmin;
+ float log_min = (scale_type == PROP_SCALE_LOG) ? max_ff(softmin, UI_PROP_SCALE_LOG_MIN) : 0.0f;
if ((but->type == UI_BTYPE_NUM) && (ui_but_is_cursor_warp(but) == false)) {
uiButNumber *number_but = (uiButNumber *)but;
+
+ if (scale_type == PROP_SCALE_LOG) {
+ log_min = max_ff(log_min, powf(10, -number_but->precision) * 0.5f);
+ }
/* Use a minimum so we have a predictable range,
* otherwise some float buttons get a large range. */
const float value_step_float_min = 0.1f;
@@ -3976,7 +3997,31 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
}
}
- data->dragfstart = (softrange == 0.0f) ? 0.0f : ((float)data->value - softmin) / softrange;
+ if (softrange == 0.0f) {
+ data->dragfstart = 0.0f;
+ }
+ else {
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ data->dragfstart = ((float)data->value - softmin) / softrange;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ BLI_assert(log_min != 0.0f);
+ const float base = softmax / log_min;
+ data->dragfstart = logf((float)data->value / log_min) / logf(base);
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubic_min = cube_f(softmin);
+ const float cubic_max = cube_f(softmax);
+ const float cubic_range = cubic_max - cubic_min;
+ const float f = ((float)data->value - softmin) * cubic_range / softrange + cubic_min;
+ data->dragfstart = (cbrtf(f) - softmin) / softrange;
+ break;
+ }
+ }
+ }
data->dragf = data->dragfstart;
data->drag_map_soft_min = softmin;
@@ -4694,6 +4739,7 @@ static float ui_numedit_apply_snapf(
/* pass */
}
else {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
float softrange = softmax - softmin;
float fac = 1.0f;
@@ -4731,31 +4777,30 @@ static float ui_numedit_apply_snapf(
}
}
- if (snap == SNAP_ON) {
- if (softrange < 2.10f) {
- tempf = roundf(tempf * 10.0f) * 0.1f;
- }
- else if (softrange < 21.0f) {
- tempf = roundf(tempf);
- }
- else {
- tempf = roundf(tempf * 0.1f) * 10.0f;
- }
- }
- else if (snap == SNAP_ON_SMALL) {
- if (softrange < 2.10f) {
- tempf = roundf(tempf * 100.0f) * 0.01f;
- }
- else if (softrange < 21.0f) {
- tempf = roundf(tempf * 10.0f) * 0.1f;
+ BLI_assert(ELEM(snap, SNAP_ON, SNAP_ON_SMALL));
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR:
+ case PROP_SCALE_CUBIC: {
+ const float snap_fac = (snap == SNAP_ON_SMALL ? 0.1f : 1.0f);
+ if (softrange < 2.10f) {
+ tempf = roundf(tempf * 10.0f / snap_fac) * 0.1f * snap_fac;
+ }
+ else if (softrange < 21.0f) {
+ tempf = roundf(tempf / snap_fac) * snap_fac;
+ }
+ else {
+ tempf = roundf(tempf * 0.1f / snap_fac) * 10.0f * snap_fac;
+ }
+ break;
}
- else {
- tempf = roundf(tempf);
+ case PROP_SCALE_LOG: {
+ const float snap_fac = powf(10.0f,
+ roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) -
+ (snap == SNAP_ON_SMALL ? 2.0f : 1.0f));
+ tempf = roundf(tempf / snap_fac) * snap_fac;
+ break;
}
}
- else {
- BLI_assert(0);
- }
if (fac != 1.0f) {
tempf *= fac;
@@ -4800,6 +4845,7 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
int lvalue, temp;
bool changed = false;
const bool is_float = ui_but_is_float(but);
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
/* prevent unwanted drag adjustments, test motion so modifier keys refresh. */
if ((is_motion || data->draglock) && (ui_but_dragedit_update_mval(data, mx) == false)) {
@@ -4811,21 +4857,74 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
const float softmax = but->softmax;
const float softrange = softmax - softmin;
+ const float log_min = (scale_type == PROP_SCALE_LOG) ?
+ max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN),
+ powf(10, -number_but->precision) * 0.5f) :
+ 0;
+
/* Mouse location isn't screen clamped to the screen so use a linear mapping
* 2px == 1-int, or 1px == 1-ClickStep */
if (is_float) {
fac *= 0.01f * number_but->step_size;
- tempf = (float)data->startvalue + ((float)(mx - data->dragstartx) * fac);
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = (float)data->startvalue + (float)(mx - data->dragstartx) * fac;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float startvalue = max_ff((float)data->startvalue, log_min);
+ tempf = expf((float)(mx - data->dragstartx) * fac) * startvalue;
+ if (tempf <= log_min) {
+ tempf = 0.0f;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ tempf = cbrtf((float)data->startvalue) + (float)(mx - data->dragstartx) * fac;
+ tempf *= tempf * tempf;
+ break;
+ }
+ }
+
tempf = ui_numedit_apply_snapf(but, tempf, softmin, softmax, snap);
#if 1 /* fake moving the click start, nicer for dragging back after passing the limit */
- if (tempf < softmin) {
- data->dragstartx -= (softmin - tempf) / fac;
- tempf = softmin;
- }
- else if (tempf > softmax) {
- data->dragstartx += (tempf - softmax) / fac;
- tempf = softmax;
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ if (tempf < softmin) {
+ data->dragstartx -= (softmin - tempf) / fac;
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx -= (softmax - tempf) / fac;
+ tempf = softmax;
+ }
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ if (tempf < log_min) {
+ data->dragstartx -= logf(log_min / (float)data->startvalue) / fac -
+ (float)(mx - data->dragstartx);
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx -= logf(softmax / (float)data->startvalue) / fac -
+ (float)(mx - data->dragstartx);
+ tempf = softmax;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ if (tempf < softmin) {
+ data->dragstartx = mx - (int)((cbrtf(softmin) - cbrtf((float)data->startvalue)) / fac);
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx = mx - (int)((cbrtf(softmax) - cbrtf((float)data->startvalue)) / fac);
+ tempf = softmax;
+ }
+ break;
+ }
}
#else
CLAMP(tempf, softmin, softmax);
@@ -4932,7 +5031,31 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
}
data->draglastx = mx;
- tempf = (softmin + data->dragf * softrange);
+
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = (softmin + data->dragf * softrange);
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float log_min = max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN),
+ powf(10.0f, -number_but->precision) * 0.5f);
+ const float base = softmax / log_min;
+ tempf = powf(base, data->dragf) * log_min;
+ if (tempf <= log_min) {
+ tempf = 0.0f;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ tempf = (softmin + data->dragf * softrange);
+ tempf *= tempf * tempf;
+ float cubic_min = softmin * softmin * softmin;
+ float cubic_max = softmax * softmax * softmax;
+ tempf = (tempf - cubic_min) / (cubic_max - cubic_min) * softrange + softmin;
+ break;
+ }
+ }
if (!is_float) {
temp = round_fl_to_int(tempf);
@@ -5179,9 +5302,19 @@ static int ui_do_but_NUM(
else {
/* Float Value. */
if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
- const double value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE;
+ double value_step;
+ if (scale_type == PROP_SCALE_LOG) {
+ value_step = powf(10.0f,
+ (roundf(log10f(data->value) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f) +
+ log10f(number_but->step_size));
+ }
+ else {
+ value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE;
+ }
BLI_assert(value_step > 0.0f);
const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ?
(double)max_ff(but->softmin,
@@ -5229,6 +5362,8 @@ static bool ui_numedit_but_SLI(uiBut *but,
return changed;
}
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
softmin = but->softmin;
softmax = but->softmax;
softrange = softmax - softmin;
@@ -5270,7 +5405,24 @@ static bool ui_numedit_but_SLI(uiBut *but,
#endif
/* done correcting mouse */
- tempf = softmin + f * softrange;
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = softmin + f * softrange;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ tempf = powf(softmax / softmin, f) * softmin;
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubicmin = cube_f(softmin);
+ const float cubicmax = cube_f(softmax);
+ const float cubicrange = cubicmax - cubicmin;
+ tempf = cube_f(softmin + f * softrange);
+ tempf = (tempf - cubicmin) / cubicrange * softrange + softmin;
+ break;
+ }
+ }
temp = round_fl_to_int(tempf);
if (snap) {
@@ -5464,6 +5616,8 @@ static int ui_do_but_SLI(
if (click) {
if (click == 2) {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
/* nudge slider to the left or right */
float f, tempf, softmin, softmax, softrange;
int temp;
@@ -5488,14 +5642,20 @@ static int ui_do_but_SLI(
f = (float)(mx - but->rect.xmin) / (BLI_rctf_size_x(&but->rect));
}
- f = softmin + f * softrange;
+ if (scale_type == PROP_SCALE_LOG) {
+ f = powf(softmax / softmin, f) * softmin;
+ }
+ else {
+ f = softmin + f * softrange;
+ }
if (!ui_but_is_float(but)) {
+ int value_step = 1;
if (f < temp) {
- temp--;
+ temp -= value_step;
}
else {
- temp++;
+ temp += value_step;
}
if (temp >= softmin && temp <= softmax) {
@@ -5506,14 +5666,23 @@ static int ui_do_but_SLI(
}
}
else {
- if (f < tempf) {
- tempf -= 0.01f;
- }
- else {
- tempf += 0.01f;
- }
-
if (tempf >= softmin && tempf <= softmax) {
+ float value_step;
+ if (scale_type == PROP_SCALE_LOG) {
+ value_step = powf(10.0f, roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f);
+ }
+ else {
+ value_step = 0.01f;
+ }
+
+ if (f < tempf) {
+ tempf -= value_step;
+ }
+ else {
+ tempf += value_step;
+ }
+
+ CLAMP(tempf, softmin, softmax);
data->value = tempf;
}
else {
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index c16c3d2c49a..4defbed940e 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -457,13 +457,15 @@ DEF_ICON_VECTOR_COLORSET_DRAW_NTH(20, 19)
# undef DEF_ICON_VECTOR_COLORSET_DRAW_NTH
static void vicon_collection_color_draw(
- short color_tag, int x, int y, int UNUSED(w), int UNUSED(h), float UNUSED(alpha))
+ short color_tag, int x, int y, int w, int UNUSED(h), float UNUSED(alpha))
{
bTheme *btheme = UI_GetTheme();
const ThemeCollectionColor *collection_color = &btheme->collection_color[color_tag];
+ const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w;
+
UI_icon_draw_ex(
- x, y, ICON_OUTLINER_COLLECTION, U.inv_dpi_fac, 1.0f, 0.0f, collection_color->color, true);
+ x, y, ICON_OUTLINER_COLLECTION, aspect, 1.0f, 0.0f, collection_color->color, true);
}
# define DEF_ICON_COLLECTION_COLOR_DRAW(index, color) \
@@ -1426,13 +1428,7 @@ static void icon_set_image(const bContext *C,
scene = CTX_data_scene(C);
}
/* Immediate version */
- ED_preview_icon_render(CTX_data_main(C),
- CTX_data_ensure_evaluated_depsgraph(C),
- scene,
- id,
- prv_img->rect[size],
- prv_img->w[size],
- prv_img->h[size]);
+ ED_preview_icon_render(C, scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]);
}
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 4c96512b4f3..23856c41ceb 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -62,7 +62,7 @@ struct wmTimer;
#define UI_MENU_PADDING (int)(0.2f * UI_UNIT_Y)
#define UI_MENU_WIDTH_MIN (UI_UNIT_Y * 9)
-/* some extra padding added to menus containing submenu icons */
+/** Some extra padding added to menus containing sub-menu icons. */
#define UI_MENU_SUBMENU_PADDING (6 * UI_DPI_FAC)
/* menu scrolling */
@@ -74,28 +74,31 @@ struct wmTimer;
#define UI_PANEL_MINX 100
#define UI_PANEL_MINY 70
-/* popover width (multiplied by 'U.widget_unit') */
+/** Popover width (multiplied by #U.widget_unit) */
#define UI_POPOVER_WIDTH_UNITS 10
-/* uiBut->flag */
+/** #uiBut.flag */
enum {
- UI_SELECT = (1 << 0), /* use when the button is pressed */
- UI_SCROLLED = (1 << 1), /* temp hidden, scrolled away */
+ /** Use when the button is pressed. */
+ UI_SELECT = (1 << 0),
+ /** Temporarily hidden (scrolled out of the view). */
+ UI_SCROLLED = (1 << 1),
UI_ACTIVE = (1 << 2),
UI_HAS_ICON = (1 << 3),
UI_HIDDEN = (1 << 4),
- UI_SELECT_DRAW = (1 << 5), /* Display selected, doesn't impact interaction. */
+ /** Display selected, doesn't impact interaction. */
+ UI_SELECT_DRAW = (1 << 5),
/** Property search filter is active and the button does not match. */
- UI_SEARCH_FILTER_NO_MATCH = (1 << 12),
- /* warn: rest of uiBut->flag in UI_interface.h */
+ UI_SEARCH_FILTER_NO_MATCH = (1 << 6),
+ /* WARNING: rest of #uiBut.flag in UI_interface.h */
};
-/* uiBut->dragflag */
+/** #uiBut.dragflag */
enum {
UI_BUT_DRAGPOIN_FREE = (1 << 0),
};
-/* but->pie_dir */
+/** #uiBut.pie_dir */
typedef enum RadialDirection {
UI_RADIAL_NONE = -1,
UI_RADIAL_N = 0,
@@ -126,13 +129,13 @@ extern const short ui_radial_dir_to_angle[8];
#define UI_BITBUT_ROW(min, max) \
(((max) >= 31 ? 0xFFFFFFFF : (1 << ((max) + 1)) - 1) - ((min) ? ((1 << (min)) - 1) : 0))
-/* split numbuts by ':' and align l/r */
+/** Split number-buttons by ':' and align left/right. */
#define USE_NUMBUTS_LR_ALIGN
-/* Use new 'align' computation code. */
+/** Use new 'align' computation code. */
#define USE_UIBUT_SPATIAL_ALIGN
-/* PieMenuData->flags */
+/** #PieMenuData.flags */
enum {
/** pie menu item collision is detected at 90 degrees */
UI_PIE_DEGREES_RANGE_LARGE = (1 << 0),
@@ -152,13 +155,13 @@ enum {
#define PIE_CLICK_THRESHOLD_SQ 50.0f
-/* max amount of items a radial menu (pie menu) can contain */
+/** The maximum number of items a radial menu (pie menu) can contain. */
#define PIE_MAX_ITEMS 8
struct uiBut {
struct uiBut *next, *prev;
- /* Pointer back to the layout item holding this button. */
+ /** Pointer back to the layout item holding this button. */
uiLayout *layout;
int flag, drawflag;
eButType type;
@@ -235,10 +238,10 @@ struct uiBut {
short modifier_key;
short iconadd;
- /* UI_BTYPE_BLOCK data */
+ /** #UI_BTYPE_BLOCK data */
uiBlockCreateFunc block_create_func;
- /* UI_BTYPE_PULLDOWN/UI_BTYPE_MENU data */
+ /** #UI_BTYPE_PULLDOWN / #UI_BTYPE_MENU data */
uiMenuCreateFunc menu_create_func;
uiMenuStepFunc menu_step_func;
@@ -252,9 +255,11 @@ struct uiBut {
struct wmOperatorType *optype;
struct PointerRNA *opptr;
short opcontext;
- uchar menu_key; /* 'a'-'z', always lower case */
- ListBase extra_op_icons; /* uiButExtraOpIcon */
+ /** When non-zero, this is the key used to activate a menu items (`a-z` always lower case). */
+ uchar menu_key;
+
+ ListBase extra_op_icons; /** #uiButExtraOpIcon */
/* Draggable data, type is WM_DRAG_... */
char dragtype;
@@ -263,10 +268,10 @@ struct uiBut {
struct ImBuf *imb;
float imb_scale;
- /* active button data */
+ /** Active button data (set when the user is hovering or interacting with a button). */
struct uiHandleButtonData *active;
- /* Custom button data. */
+ /** Custom button data (borrowed, not owned). */
void *custom_data;
char *editstr;
@@ -429,7 +434,7 @@ struct PieMenuData {
float alphafac;
};
-/* uiBlock.content_hints */
+/** #uiBlock.content_hints */
enum eBlockContentHints {
/** In a menu block, if there is a single sub-menu button, we add some
* padding to the right to put nicely aligned triangle icons there. */
@@ -463,7 +468,8 @@ struct uiBlock {
struct Panel *panel;
uiBlock *oldblock;
- ListBase butstore; /* UI_butstore_* runtime function */
+ /** Used for `UI_butstore_*` runtime function. */
+ ListBase butstore;
ListBase button_groups; /* #uiButtonGroup. */
@@ -479,7 +485,8 @@ struct uiBlock {
rctf rect;
float aspect;
- uint puphash; /* popup menu hash for memory */
+ /** Unique hash used to implement popup menu memory. */
+ uint puphash;
uiButHandleFunc func;
void *func_arg1;
@@ -494,10 +501,10 @@ struct uiBlock {
uiBlockHandleFunc handle_func;
void *handle_func_arg;
- /* custom extra handling */
+ /** Custom extra event handling. */
int (*block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *);
- /* extra draw function for custom blocks */
+ /** Custom extra draw function for custom blocks. */
void (*drawextra)(const struct bContext *C, void *idv, void *arg1, void *arg2, rcti *rect);
void *drawextra_arg1;
void *drawextra_arg2;
@@ -507,7 +514,7 @@ struct uiBlock {
/** Hints about the buttons of this block. Used to avoid iterating over
* buttons to find out if some criteria is met by any. Instead, check this
* criteria when adding the button and set a flag here if it's met. */
- short content_hints; /* eBlockContentHints */
+ short content_hints; /* #eBlockContentHints */
char direction;
/** UI_BLOCK_THEME_STYLE_* */
@@ -521,11 +528,11 @@ struct uiBlock {
const char *lockstr;
bool lock;
- /** to keep blocks while drawing and free them afterwards */
+ /** To keep blocks while drawing and free them afterwards. */
bool active;
- /** to avoid tooltip after click */
+ /** To avoid tool-tip after click. */
bool tooltipdisabled;
- /** UI_block_end done? */
+ /** True when #UI_block_end has been called. */
bool endblock;
/** for doing delayed */
@@ -535,12 +542,12 @@ struct uiBlock {
/** for doing delayed */
int bounds, minbounds;
- /** pull-downs, to detect outside, can differ per case how it is created. */
+ /** Pull-downs, to detect outside, can differ per case how it is created. */
rctf safety;
- /** uiSafetyRct list */
+ /** #uiSafetyRct list */
ListBase saferct;
- uiPopupBlockHandle *handle; /* handle */
+ uiPopupBlockHandle *handle;
/** use so presets can find the operator,
* across menus and from nested popups which fail for operator context. */
@@ -555,10 +562,12 @@ struct uiBlock {
/** \note only accessed by color picker templates. */
ColorPickerData color_pickers;
- bool is_color_gamma_picker; /* Block for color picker with gamma baked in. */
+ /** Block for color picker with gamma baked in. */
+ bool is_color_gamma_picker;
- /** display device name used to display this block,
- * used by color widgets to transform colors from/to scene linear
+ /**
+ * Display device name used to display this block,
+ * used by color widgets to transform colors from/to scene linear.
*/
char display_device[64];
@@ -651,6 +660,7 @@ bool ui_but_context_poll_operator(struct bContext *C, struct wmOperatorType *ot,
extern void ui_but_update(uiBut *but);
extern void ui_but_update_edited(uiBut *but);
+extern PropertyScaleType ui_but_scale_type(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_float(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_bool(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_unit(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
@@ -671,9 +681,9 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]);
/* interface_regions.c */
struct uiKeyNavLock {
- /* Set when we're using key-input. */
+ /** Set when we're using keyboard-input. */
bool is_keynav;
- /* only used to check if we've moved the cursor */
+ /** Only used to check if we've moved the cursor. */
int event_xy[2];
};
@@ -689,7 +699,7 @@ struct uiPopupBlockCreate {
int event_xy[2];
- /* when popup is initialized from a button */
+ /** Set when popup is initialized from a button. */
struct ARegion *butregion;
uiBut *but;
};
@@ -698,7 +708,7 @@ struct uiPopupBlockHandle {
/* internal */
struct ARegion *region;
- /* use only for 'UI_BLOCK_MOVEMOUSE_QUIT' popups */
+ /** Use only for #UI_BLOCK_MOVEMOUSE_QUIT popups. */
float towards_xy[2];
double towardstime;
bool dotowards;
@@ -708,9 +718,9 @@ struct uiPopupBlockHandle {
void (*cancel_func)(struct bContext *C, void *arg);
void *popup_arg;
- /* store data for refreshing popups */
+ /** Store data for refreshing popups. */
struct uiPopupBlockCreate popup_create_vars;
- /* true if we can re-create the popup using 'popup_create_vars' */
+ /** True if we can re-create the popup using #uiPopupBlockHandle.popup_create_vars. */
bool can_refresh;
bool refresh;
@@ -730,7 +740,7 @@ struct uiPopupBlockHandle {
int retvalue;
float retvec[4];
- /* menu direction */
+ /** Menu direction. */
int direction;
/* Previous values so we don't resize or reposition on refresh. */
@@ -924,9 +934,7 @@ extern void ui_but_execute_end(struct bContext *C,
void *active_back);
extern void ui_but_active_free(const struct bContext *C, uiBut *but);
extern int ui_but_menu_direction(uiBut *but);
-extern void ui_but_text_password_hide(char password_str[UI_MAX_DRAW_STR],
- uiBut *but,
- const bool restore);
+extern void ui_but_text_password_hide(char password_str[128], uiBut *but, const bool restore);
extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction);
bool ui_but_is_editing(const uiBut *but);
float ui_block_calc_pie_segment(struct uiBlock *block, const float event_xy[2]);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 4430d00f2e3..c04432a2912 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2718,6 +2718,7 @@ uiBut *ui_but_add_search(
ui_searchbox_create_generic,
ui_rna_collection_search_update_fn,
coll_search,
+ false,
ui_rna_collection_search_arg_free_fn,
NULL,
NULL);
@@ -3037,7 +3038,7 @@ void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, i
/* popover */
void uiItemPopoverPanel_ptr(
- uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon)
+ uiLayout *layout, const bContext *C, PanelType *pt, const char *name, int icon)
{
if (!name) {
name = CTX_IFACE_(pt->translation_context, pt->label);
@@ -3066,7 +3067,7 @@ void uiItemPopoverPanel_ptr(
}
void uiItemPopoverPanel(
- uiLayout *layout, bContext *C, const char *panel_type, const char *name, int icon)
+ uiLayout *layout, const bContext *C, const char *panel_type, const char *name, int icon)
{
PanelType *pt = WM_paneltype_find(panel_type, true);
if (pt == NULL) {
@@ -4041,12 +4042,11 @@ static void ui_litem_layout_column_flow(uiLayout *litem)
int emy = 0;
int miny = 0;
- int w = litem->w - (flow->totcol - 1) * style->columnspace;
emh = toth / flow->totcol;
/* create column per column */
col = 0;
- w = (litem->w - (flow->totcol - 1) * style->columnspace) / flow->totcol;
+ int w = (litem->w - (flow->totcol - 1) * style->columnspace) / flow->totcol;
LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 540e98f542e..0cf3ad59903 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -72,6 +72,7 @@
#include "BKE_main.h"
#include "BLI_ghash.h"
#include "ED_screen.h"
+#include "ED_text.h"
/* -------------------------------------------------------------------- */
/** \name Copy Data Path Operator
@@ -1336,18 +1337,23 @@ static int editsource_text_edit(bContext *C,
return OPERATOR_CANCELLED;
}
+ txt_move_toline(text, line - 1, false);
+
/* naughty!, find text area to set, not good behavior
* but since this is a dev tool lets allow it - campbell */
ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0);
if (area) {
SpaceText *st = area->spacedata.first;
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
st->text = text;
+ if (region) {
+ ED_text_scroll_to_cursor(st, region, true);
+ }
}
else {
BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2);
}
- txt_move_toline(text, line - 1, false);
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
return OPERATOR_FINISHED;
@@ -1374,7 +1380,9 @@ static int editsource_exec(bContext *C, wmOperator *op)
/* redraw and get active button python info */
ED_region_do_layout(C, region);
+ WM_draw_region_viewport_bind(region);
ED_region_do_draw(C, region);
+ WM_draw_region_viewport_unbind(region);
region->do_draw = false;
for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash);
@@ -1487,117 +1495,115 @@ static void edittranslation_find_po_file(const char *root,
static int edittranslation_exec(bContext *C, wmOperator *op)
{
uiBut *but = UI_context_active_but_get(C);
- int ret = OPERATOR_CANCELLED;
-
- if (but) {
- wmOperatorType *ot;
- PointerRNA ptr;
- char popath[FILE_MAX];
- const char *root = U.i18ndir;
- const char *uilng = BLT_lang_get();
-
- uiStringInfo but_label = {BUT_GET_LABEL, NULL};
- uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
- uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
- uiStringInfo but_tip = {BUT_GET_TIP, NULL};
- uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
- uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
- uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
- uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
- uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
- uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
-
- if (!BLI_is_dir(root)) {
- BKE_report(op->reports,
- RPT_ERROR,
- "Please set your Preferences' 'Translation Branches "
- "Directory' path to a valid directory");
- return OPERATOR_CANCELLED;
- }
- ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0);
- if (ot == NULL) {
- BKE_reportf(op->reports,
- RPT_ERROR,
- "Could not find operator '%s'! Please enable ui_translate add-on "
- "in the User Preferences",
- EDTSRC_I18N_OP_NAME);
- return OPERATOR_CANCELLED;
- }
- /* Try to find a valid po file for current language... */
- edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
- /* printf("po path: %s\n", popath); */
- if (popath[0] == '\0') {
- BKE_reportf(
- op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
- return OPERATOR_CANCELLED;
- }
-
- UI_but_string_info_get(C,
- but,
- &but_label,
- &rna_label,
- &enum_label,
- &but_tip,
- &rna_tip,
- &enum_tip,
- &rna_struct,
- &rna_prop,
- &rna_enum,
- &rna_ctxt,
- NULL);
-
- WM_operator_properties_create_ptr(&ptr, ot);
- RNA_string_set(&ptr, "lang", uilng);
- RNA_string_set(&ptr, "po_file", popath);
- RNA_string_set(&ptr, "but_label", but_label.strinfo);
- RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
- RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
- RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
- RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
- RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
- RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
- RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
- RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
- RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
- ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
-
- /* Clean up */
- if (but_label.strinfo) {
- MEM_freeN(but_label.strinfo);
- }
- if (rna_label.strinfo) {
- MEM_freeN(rna_label.strinfo);
- }
- if (enum_label.strinfo) {
- MEM_freeN(enum_label.strinfo);
- }
- if (but_tip.strinfo) {
- MEM_freeN(but_tip.strinfo);
- }
- if (rna_tip.strinfo) {
- MEM_freeN(rna_tip.strinfo);
- }
- if (enum_tip.strinfo) {
- MEM_freeN(enum_tip.strinfo);
- }
- if (rna_struct.strinfo) {
- MEM_freeN(rna_struct.strinfo);
- }
- if (rna_prop.strinfo) {
- MEM_freeN(rna_prop.strinfo);
- }
- if (rna_enum.strinfo) {
- MEM_freeN(rna_enum.strinfo);
- }
- if (rna_ctxt.strinfo) {
- MEM_freeN(rna_ctxt.strinfo);
- }
+ if (but == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Active button not found");
+ return OPERATOR_CANCELLED;
+ }
- return ret;
+ wmOperatorType *ot;
+ PointerRNA ptr;
+ char popath[FILE_MAX];
+ const char *root = U.i18ndir;
+ const char *uilng = BLT_lang_get();
+
+ uiStringInfo but_label = {BUT_GET_LABEL, NULL};
+ uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
+ uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
+ uiStringInfo but_tip = {BUT_GET_TIP, NULL};
+ uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
+ uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
+ uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
+ uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
+ uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
+ uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
+
+ if (!BLI_is_dir(root)) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Please set your Preferences' 'Translation Branches "
+ "Directory' path to a valid directory");
+ return OPERATOR_CANCELLED;
+ }
+ ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0);
+ if (ot == NULL) {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Could not find operator '%s'! Please enable ui_translate add-on "
+ "in the User Preferences",
+ EDTSRC_I18N_OP_NAME);
+ return OPERATOR_CANCELLED;
+ }
+ /* Try to find a valid po file for current language... */
+ edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
+ /* printf("po path: %s\n", popath); */
+ if (popath[0] == '\0') {
+ BKE_reportf(
+ op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
+ return OPERATOR_CANCELLED;
}
- BKE_report(op->reports, RPT_ERROR, "Active button not found");
- return OPERATOR_CANCELLED;
+ UI_but_string_info_get(C,
+ but,
+ &but_label,
+ &rna_label,
+ &enum_label,
+ &but_tip,
+ &rna_tip,
+ &enum_tip,
+ &rna_struct,
+ &rna_prop,
+ &rna_enum,
+ &rna_ctxt,
+ NULL);
+
+ WM_operator_properties_create_ptr(&ptr, ot);
+ RNA_string_set(&ptr, "lang", uilng);
+ RNA_string_set(&ptr, "po_file", popath);
+ RNA_string_set(&ptr, "but_label", but_label.strinfo);
+ RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
+ RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
+ RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
+ RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
+ RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
+ RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
+ RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
+ RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
+ RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
+ const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
+
+ /* Clean up */
+ if (but_label.strinfo) {
+ MEM_freeN(but_label.strinfo);
+ }
+ if (rna_label.strinfo) {
+ MEM_freeN(rna_label.strinfo);
+ }
+ if (enum_label.strinfo) {
+ MEM_freeN(enum_label.strinfo);
+ }
+ if (but_tip.strinfo) {
+ MEM_freeN(but_tip.strinfo);
+ }
+ if (rna_tip.strinfo) {
+ MEM_freeN(rna_tip.strinfo);
+ }
+ if (enum_tip.strinfo) {
+ MEM_freeN(enum_tip.strinfo);
+ }
+ if (rna_struct.strinfo) {
+ MEM_freeN(rna_struct.strinfo);
+ }
+ if (rna_prop.strinfo) {
+ MEM_freeN(rna_prop.strinfo);
+ }
+ if (rna_enum.strinfo) {
+ MEM_freeN(rna_enum.strinfo);
+ }
+ if (rna_ctxt.strinfo) {
+ MEM_freeN(rna_ctxt.strinfo);
+ }
+
+ return ret;
}
static void UI_OT_edittranslation_init(wmOperatorType *ot)
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 7343417137a..6505a7cd76a 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -437,15 +437,21 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr
/* Find how many instanced panels with this context string. */
int list_panels_len = 0;
+ int start_index = -1;
LISTBASE_FOREACH (const Panel *, panel, &region->panels) {
if (panel->type) {
if (panel->type->flag & PANEL_TYPE_INSTANCED) {
if (panel_type_context_poll(region, panel->type, context)) {
+ if (panel == drag_panel) {
+ BLI_assert(start_index == -1); /* This panel should only appear once. */
+ start_index = list_panels_len;
+ }
list_panels_len++;
}
}
}
}
+ BLI_assert(start_index != -1); /* The drag panel should definitely be in the list. */
/* Sort the matching instanced panels by their display order. */
PanelSort *panel_sort = MEM_callocN(list_panels_len * sizeof(*panel_sort), __func__);
@@ -472,6 +478,11 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr
MEM_freeN(panel_sort);
+ if (move_to_index == start_index) {
+ /* In this case, the reorder was not changed, so don't do any updates or call the callback. */
+ return;
+ }
+
/* Set the bit to tell the interface to instanced the list. */
drag_panel->flag |= PNL_INSTANCED_LIST_ORDER_CHANGED;
@@ -2529,9 +2540,8 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm
{
ARegion *region = CTX_wm_region(C);
- Panel *panel = NULL;
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
- panel = block->panel;
+ Panel *panel = block->panel;
if (panel == NULL) {
continue;
}
@@ -2541,15 +2551,11 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm
ui_window_to_block(region, block, &mx, &my);
const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) {
- break;
+ return UI_panel_custom_data_get(panel);
}
}
- if (panel == NULL) {
- return NULL;
- }
-
- return UI_panel_custom_data_get(panel);
+ return NULL;
}
/** \} */
diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c
index f234f0fbbf5..58a74a3473e 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.c
+++ b/source/blender/editors/interface/interface_region_menu_popup.c
@@ -86,6 +86,17 @@ int ui_but_menu_step(uiBut *but, int direction)
return 0;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Popup Menu Memory
+ *
+ * Support menu-memory, a feature that positions the cursor
+ * over the previously used menu item.
+ *
+ * \note This is stored for each unique menu title.
+ * \{ */
+
static uint ui_popup_string_hash(const char *str, const bool use_sep)
{
/* sometimes button contains hotkey, sometimes not, strip for proper compare */
@@ -392,7 +403,7 @@ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C,
pup->layout = UI_block_layout(
pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style);
- /* note, this intentionally differs from the menu & submenu default because many operators
+ /* note, this intentionally differs from the menu & sub-menu default because many operators
* use popups like this to select one of their options -
* where having invoke doesn't make sense */
uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c
index 12044863b8c..987cde61f97 100644
--- a/source/blender/editors/interface/interface_region_search.c
+++ b/source/blender/editors/interface/interface_region_search.c
@@ -677,8 +677,7 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
}
/* The previous menu item draws the active selection. */
- ui_draw_menu_item(
- &data->fstyle, &rect, name_sep, icon, state & ~UI_ACTIVE, separator_type, NULL);
+ ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, NULL);
}
}
/* indicate more */
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 244cf7622e0..accfb78ab94 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -428,7 +428,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
if (STREQ(expr_result, "")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -485,7 +485,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
if (STREQ(expr_result, ".")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -589,7 +589,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
shortcut = BLI_strdup(has_valid_context_error);
}
- else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_intptr(C, expr_imports, expr, NULL, &expr_result)) {
if (expr_result != 0) {
wmKeyMap *keymap = (wmKeyMap *)expr_result;
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
@@ -654,7 +654,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* pass */
}
else if (BPY_run_string_as_string_and_size(
- C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) {
+ C, expr_imports, expr, NULL, &expr_result, &expr_result_len)) {
/* pass. */
}
}
@@ -731,7 +731,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
/* pass */
}
- else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_intptr(C, expr_imports, expr, NULL, &expr_result)) {
if (expr_result != 0) {
{
uiTooltipField *field = text_field_add(data,
@@ -947,12 +947,13 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
/* button is disabled, we may be able to tell user why */
if (but->flag & UI_BUT_DISABLED) {
const char *disabled_msg = NULL;
+ bool disabled_msg_free = false;
/* if operator poll check failed, it can give pretty precise info why */
if (but->optype) {
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
WM_operator_poll_context(C, but->optype, but->opcontext);
- disabled_msg = CTX_wm_operator_poll_msg_get(C);
+ disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free);
}
/* alternatively, buttons can store some reasoning too */
else if (but->disabled_info) {
@@ -967,6 +968,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
});
field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg);
}
+ if (disabled_msg_free) {
+ MEM_freeN((void *)disabled_msg);
+ }
}
if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) {
@@ -1469,9 +1473,10 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
*/
if (gz->type->screen_bounds_get) {
rcti bounds;
- gz->type->screen_bounds_get(C, gz, &bounds);
- init_position[0] = bounds.xmin;
- init_position[1] = bounds.ymin;
+ if (gz->type->screen_bounds_get(C, gz, &bounds)) {
+ init_position[0] = bounds.xmin;
+ init_position[1] = bounds.ymin;
+ }
}
return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect);
diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c
index ff42d434f29..91ad6619889 100644
--- a/source/blender/editors/interface/interface_template_search_menu.c
+++ b/source/blender/editors/interface/interface_template_search_menu.c
@@ -510,11 +510,19 @@ static struct MenuSearch_Data *menu_items_from_ui_create(
const char *global_menu_prefix = NULL;
if (include_all_areas) {
+ bScreen *screen = WM_window_get_active_screen(win);
+
/* First create arrays for ui_type. */
PropertyRNA *prop_ui_type = NULL;
{
+ /* This must be a valid pointer, with only it's type checked. */
+ ScrArea area_dummy = {
+ /* Anything besides #SPACE_EMPTY is fine,
+ * as this value is only included in the enum when set. */
+ .spacetype = SPACE_TOPBAR,
+ };
PointerRNA ptr;
- RNA_pointer_create(NULL, &RNA_Area, NULL, &ptr);
+ RNA_pointer_create(&screen->id, &RNA_Area, &area_dummy, &ptr);
prop_ui_type = RNA_struct_find_property(&ptr, "ui_type");
RNA_property_enum_items(C,
&ptr,
@@ -529,7 +537,6 @@ static struct MenuSearch_Data *menu_items_from_ui_create(
}
}
- bScreen *screen = WM_window_get_active_screen(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (region != NULL) {
@@ -1141,6 +1148,7 @@ void UI_but_func_menu_search(uiBut *but)
ui_searchbox_create_menu,
menu_search_update_fn,
data,
+ false,
menu_search_arg_free_fn,
menu_search_exec_fn,
NULL);
diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.c
index 2c83f184ff0..2b765a1a2f5 100644
--- a/source/blender/editors/interface/interface_template_search_operator.c
+++ b/source/blender/editors/interface/interface_template_search_operator.c
@@ -121,6 +121,7 @@ void UI_but_func_operator_search(uiBut *but)
operator_search_update_fn,
NULL,
false,
+ NULL,
operator_search_exec_fn,
NULL);
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 2d1663a3ecd..3990ad68c4d 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -309,6 +309,7 @@ static uiBlock *template_common_search_menu(const bContext *C,
ui_searchbox_create_generic,
search_update_fn,
search_arg,
+ false,
NULL,
search_exec_fn,
active_item);
@@ -652,7 +653,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
/* Only remap that specific ID usage to overriding local data-block. */
ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false);
if (override_id != NULL) {
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
if (GS(override_id->name) == ID_OB) {
Scene *scene = CTX_data_scene(C);
@@ -671,7 +672,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
}
else {
if (BKE_lib_id_make_local(bmain, id, false, 0)) {
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
/* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
@@ -1077,7 +1078,7 @@ static void template_ID(const bContext *C,
char numstr[32];
short numstr_len;
- numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id));
+ numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id));
but = uiDefBut(
block,
@@ -1109,7 +1110,7 @@ static void template_ID(const bContext *C,
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
- if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS)) &&
+ if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_OB, ID_WS)) &&
(hide_buttons == false)) {
uiDefIconButR(block,
UI_BTYPE_ICON_TOGGLE,
@@ -6829,6 +6830,7 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
uiLayout *ui_abs = uiLayoutAbsolute(layout, false);
uiBlock *block = uiLayoutGetBlock(ui_abs);
+ eUIEmbossType previous_emboss = UI_block_emboss_get(block);
UI_fontstyle_set(&style->widgetlabel);
int width = BLF_width(style->widgetlabel.uifont_id, report->message, report->len);
@@ -6903,6 +6905,8 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
width + UI_UNIT_X,
UI_UNIT_Y,
"Show in Info Log");
+
+ UI_block_emboss_set(block, previous_emboss);
}
void uiTemplateInputStatus(uiLayout *layout, struct bContext *C)
@@ -7191,7 +7195,7 @@ void uiTemplateComponentMenu(uiLayout *layout,
/** \name Node Socket Icon Template
* \{ */
-void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color)
+void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float color[4])
{
uiBlock *block = uiLayoutGetBlock(layout);
UI_block_align_begin(block);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 0c6be7b1196..fe6a8b0d1a6 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -1225,12 +1225,6 @@ static bool draw_widgetbase_batch_skip_draw_cache(void)
return true;
}
- /* There are also reports that some AMD and Mesa driver configuration suffer from the
- * same issue, T78803. */
- if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
- return true;
- }
-
return false;
}
@@ -1936,19 +1930,27 @@ static void widget_draw_text_ime_underline(const uiFontStyle *fstyle,
}
#endif /* WITH_INPUT_IME */
-static bool widget_draw_text_underline_calc_center_x(const char *UNUSED(str),
+struct UnderlineData {
+ size_t str_offset; /* The string offset of the underlined character. */
+ int width_px; /* The underline width in pixels. */
+ int r_offset_px[2]; /* Write the X,Y offset here. */
+};
+
+static bool widget_draw_text_underline_calc_position(const char *UNUSED(str),
const size_t str_step_ofs,
const rcti *glyph_step_bounds,
const int UNUSED(glyph_advance_x),
const rctf *glyph_bounds,
- const int glyph_bearing[2],
+ const int UNUSED(glyph_bearing[2]),
void *user_data)
{
- /* The index of the character to get, set to the x-position. */
- int *ul_data = user_data;
- if (ul_data[0] == (int)str_step_ofs) {
- ul_data[1] = glyph_step_bounds->xmin + glyph_bearing[0] +
- (BLI_rctf_size_x(glyph_bounds) / 2.0f);
+ struct UnderlineData *ul_data = user_data;
+ if (ul_data->str_offset == str_step_ofs) {
+ /* Full width of this glyph including both bearings. */
+ const float width = glyph_bounds->xmin + BLI_rctf_size_x(glyph_bounds) + glyph_bounds->xmin;
+ ul_data->r_offset_px[0] = glyph_step_bounds->xmin + ((width - ul_data->width_px) * 0.5f);
+ /* Two line-widths below the lower glyph bounds. */
+ ul_data->r_offset_px[1] = glyph_bounds->ymin - U.pixelsize - U.pixelsize;
/* Early exit. */
return false;
}
@@ -2204,23 +2206,30 @@ static void widget_draw_text(const uiFontStyle *fstyle,
BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
}
- int ul_data[2] = {
- ul_index, /* Character index to test. */
- 0, /* Write the x-offset here. */
+ int ul_width = round_fl_to_int(BLF_width(fstyle->uifont_id, "_", 2));
+ int ul_height = max_ii(fstyle->points * U.dpi_fac * 0.1f, U.pixelsize);
+
+ struct UnderlineData ul_data = {
+ .str_offset = ul_index,
+ .width_px = ul_width,
};
+
BLF_boundbox_foreach_glyph(fstyle->uifont_id,
drawstr_ofs,
ul_index + 1,
- widget_draw_text_underline_calc_center_x,
- ul_data);
- ul_data[1] -= BLF_width(fstyle->uifont_id, "_", 2) / 2.0f;
-
- BLF_position(fstyle->uifont_id,
- rect->xmin + font_xofs + ul_data[1],
- rect->ymin + font_yofs,
- 0.0f);
- BLF_color4ubv(fstyle->uifont_id, wcol->text);
- BLF_draw(fstyle->uifont_id, "_", 2);
+ widget_draw_text_underline_calc_position,
+ &ul_data);
+
+ GPU_blend(GPU_BLEND_ALPHA);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformColor4ubv(wcol->text);
+ const int pos_x = rect->xmin + font_xofs + ul_data.r_offset_px[0];
+ const int pos_y = rect->ymin + font_yofs + ul_data.r_offset_px[1];
+ immRecti(pos, pos_x, pos_y, pos_x + ul_width, pos_y - ul_height);
+ immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
if (fstyle->kerning == 1) {
BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
@@ -2422,11 +2431,12 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
but->block->aspect = aspect_orig;
#endif
- rect->xmin += icon_size + icon_padding;
+ rect->xmin += round_fl_to_int(icon_size + icon_padding);
}
if (!no_text_padding) {
- const int text_padding = (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect;
+ const int text_padding = round_fl_to_int((UI_TEXT_MARGIN_X * U.widget_unit) /
+ but->block->aspect);
if (but->editstr) {
rect->xmin += text_padding;
}
@@ -3757,12 +3767,35 @@ static void widget_numslider(
float factor, factor_ui;
float factor_discard = 1.0f; /* No discard. */
const float value = (float)ui_but_value_get(but);
-
- if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
- factor = value / but->softmax;
- }
- else {
- factor = (value - but->softmin) / (but->softmax - but->softmin);
+ const float softmin = but->softmin;
+ const float softmax = but->softmax;
+ const float softrange = softmax - softmin;
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
+ factor = value / softmax;
+ }
+ else {
+ factor = (value - softmin) / softrange;
+ }
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float logmin = fmaxf(softmin, 0.5e-8f);
+ const float base = softmax / logmin;
+ factor = logf(value / logmin) / logf(base);
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubicmin = cube_f(softmin);
+ const float cubicmax = cube_f(softmax);
+ const float cubicrange = cubicmax - cubicmin;
+ const float f = (value - softmin) * cubicrange / softrange + cubicmin;
+ factor = (cbrtf(f) - softmin) / softrange;
+ break;
+ }
}
const float width = (float)BLI_rcti_size_x(rect);
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 7453cd17868..40c510af7e5 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -757,7 +757,7 @@ typedef struct v2dViewZoomData {
} v2dViewZoomData;
/**
- * Clamp by convention rather then locking flags,
+ * Clamp by convention rather than locking flags,
* for ndof and +/- keys
*/
static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index d45c7ca9b75..44b5f85050f 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -40,8 +40,8 @@ set(SRC
io_alembic.c
io_cache.c
io_collada.c
- io_gpencil_import.c
io_gpencil_export.c
+ io_gpencil_import.c
io_gpencil_utils.c
io_ops.c
io_usd.c
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index a66f53ea839..28838d677f0 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -121,6 +121,7 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
.uvs = RNA_boolean_get(op->ptr, "uvs"),
.normals = RNA_boolean_get(op->ptr, "normals"),
.vcolors = RNA_boolean_get(op->ptr, "vcolors"),
+ .orcos = RNA_boolean_get(op->ptr, "orcos"),
.apply_subdiv = RNA_boolean_get(op->ptr, "apply_subdiv"),
.curves_as_mesh = RNA_boolean_get(op->ptr, "curves_as_mesh"),
.flatten_hierarchy = RNA_boolean_get(op->ptr, "flatten"),
@@ -210,6 +211,7 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(col, imfptr, "normals", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "vcolors", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "orcos", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "face_sets", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "curves_as_mesh", 0, NULL, ICON_NONE);
@@ -240,21 +242,17 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_alembic_export_draw(bContext *C, wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
/* Conveniently set start and end frame to match the scene's frame range. */
Scene *scene = CTX_data_scene(C);
- if (scene != NULL && RNA_boolean_get(&ptr, "init_scene_frame_range")) {
- RNA_int_set(&ptr, "start", SFRA);
- RNA_int_set(&ptr, "end", EFRA);
+ if (scene != NULL && RNA_boolean_get(op->ptr, "init_scene_frame_range")) {
+ RNA_int_set(op->ptr, "start", SFRA);
+ RNA_int_set(op->ptr, "end", EFRA);
- RNA_boolean_set(&ptr, "init_scene_frame_range", false);
+ RNA_boolean_set(op->ptr, "init_scene_frame_range", false);
}
- ui_alembic_export_settings(op->layout, &ptr);
+ ui_alembic_export_settings(op->layout, op->ptr);
}
static bool wm_alembic_export_check(bContext *UNUSED(C), wmOperator *op)
@@ -382,6 +380,12 @@ void WM_OT_alembic_export(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex Colors", "Export vertex colors");
+ RNA_def_boolean(ot->srna,
+ "orcos",
+ true,
+ "Generated Coordinates",
+ "Export undeformed mesh vertex coordinates");
+
RNA_def_boolean(
ot->srna, "face_sets", 0, "Face Sets", "Export per face shading group assignments");
@@ -595,10 +599,7 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- ui_alembic_import_settings(op->layout, &ptr);
+ ui_alembic_import_settings(op->layout, op->ptr);
}
/* op->invoke, opens fileselect if path property not set, otherwise executes */
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index 2bf975cc4f5..859c12d7e52 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -402,10 +402,7 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
static void wm_collada_export_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- uiCollada_exportSettings(op->layout, &ptr);
+ uiCollada_exportSettings(op->layout, op->ptr);
}
static bool wm_collada_export_check(bContext *UNUSED(C), wmOperator *op)
@@ -799,10 +796,7 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr)
static void wm_collada_import_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- uiCollada_importSettings(op->layout, &ptr);
+ uiCollada_importSettings(op->layout, op->ptr);
}
void WM_OT_collada_import(wmOperatorType *ot)
diff --git a/source/blender/editors/io/io_gpencil.h b/source/blender/editors/io/io_gpencil.h
index 98cb8b13310..b347be00412 100644
--- a/source/blender/editors/io/io_gpencil.h
+++ b/source/blender/editors/io/io_gpencil.h
@@ -27,7 +27,6 @@
struct ARegion;
struct bContext;
struct View3D;
-struct wmOperator;
struct wmOperatorType;
void WM_OT_gpencil_import_svg(struct wmOperatorType *ot);
diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c
index 98e30f51116..b49be324372 100644
--- a/source/blender/editors/io/io_gpencil_export.c
+++ b/source/blender/editors/io/io_gpencil_export.c
@@ -50,6 +50,7 @@
#include "gpencil_io.h"
+#if defined(WITH_PUGIXML) || defined(WITH_HARU)
/* Definition of enum elements to export. */
/* Common props for exporting. */
static void gpencil_export_common_props_definition(wmOperatorType *ot)
@@ -102,6 +103,7 @@ static void set_export_filepath(bContext *C, wmOperator *op, const char *extensi
RNA_string_set(op->ptr, "filepath", filepath);
}
}
+#endif
/* <-------- SVG single frame export. --------> */
#ifdef WITH_PUGIXML
@@ -215,11 +217,7 @@ static void ui_gpencil_export_svg_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_gpencil_export_svg_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_export_svg_settings(op->layout, &ptr);
+ ui_gpencil_export_svg_settings(op->layout, op->ptr);
}
static bool wm_gpencil_export_svg_poll(bContext *C)
@@ -377,11 +375,7 @@ static void ui_gpencil_export_pdf_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_gpencil_export_pdf_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_export_pdf_settings(op->layout, &ptr);
+ ui_gpencil_export_pdf_settings(op->layout, op->ptr);
}
static bool wm_gpencil_export_pdf_poll(bContext *C)
diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c
index 9768da85940..a9911f1cef2 100644
--- a/source/blender/editors/io/io_gpencil_import.c
+++ b/source/blender/editors/io/io_gpencil_import.c
@@ -138,10 +138,7 @@ static void ui_gpencil_import_svg_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_gpencil_import_svg_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_import_svg_settings(op->layout, &ptr);
+ ui_gpencil_import_svg_settings(op->layout, op->ptr);
}
static bool wm_gpencil_import_svg_poll(bContext *C)
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index 1226cc57359..36edbbe31d6 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -26,6 +26,7 @@
#include "BLI_math.h"
#include "BKE_context.h"
+#include "BKE_curve.h"
#include "BKE_mask.h"
#include "DEG_depsgraph.h"
@@ -692,6 +693,33 @@ void MASK_OT_add_feather_vertex(wmOperatorType *ot)
/******************** common primitive functions *********************/
+static BezTriple *points_to_bezier(const float (*points)[2],
+ const int num_points,
+ const char handle_type,
+ const float scale,
+ const float location[2])
+{
+ BezTriple *bezier_points = MEM_calloc_arrayN(num_points, sizeof(BezTriple), __func__);
+ for (int i = 0; i < num_points; i++) {
+ copy_v2_v2(bezier_points[i].vec[1], points[i]);
+ mul_v2_fl(bezier_points[i].vec[1], scale);
+ add_v2_v2(bezier_points[i].vec[1], location);
+
+ bezier_points[i].h1 = handle_type;
+ bezier_points[i].h2 = handle_type;
+ }
+
+ for (int i = 0; i < num_points; i++) {
+ BKE_nurb_handle_calc(&bezier_points[i],
+ &bezier_points[(i - 1 + num_points) % num_points],
+ &bezier_points[(i + 1) % num_points],
+ false,
+ false);
+ }
+
+ return bezier_points;
+}
+
static int create_primitive_from_points(
bContext *C, wmOperator *op, const float (*points)[2], int num_points, char handle_type)
{
@@ -734,25 +762,25 @@ static int create_primitive_from_points(
const int spline_index = BKE_mask_layer_shape_spline_to_index(mask_layer, new_spline);
+ BezTriple *bezier_points = points_to_bezier(points, num_points, handle_type, scale, location);
+
for (int i = 0; i < num_points; i++) {
new_spline->tot_point = i + 1;
MaskSplinePoint *new_point = &new_spline->points[i];
BKE_mask_parent_init(&new_point->parent);
- copy_v2_v2(new_point->bezt.vec[1], points[i]);
- mul_v2_fl(new_point->bezt.vec[1], scale);
- add_v2_v2(new_point->bezt.vec[1], location);
+ new_point->bezt = bezier_points[i];
- new_point->bezt.h1 = handle_type;
- new_point->bezt.h2 = handle_type;
BKE_mask_point_select_set(new_point, true);
if (mask_layer->splines_shapes.first) {
- BKE_mask_layer_shape_changed_add(mask_layer, spline_index + i, true, true);
+ BKE_mask_layer_shape_changed_add(mask_layer, spline_index + i, true, false);
}
}
+ MEM_freeN(bezier_points);
+
if (added_mask) {
WM_event_add_notifier(C, NC_MASK | NA_ADDED, NULL);
}
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index 1eb6613d8fe..81bf66da72c 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -220,8 +220,8 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot)
}
/*
- * - loop over selected shapekeys.
- * - find firstsel/lastsel pairs.
+ * - loop over selected shape-keys.
+ * - find first-selected/last-selected pairs.
* - move these into a temp list.
* - re-key all the original shapes.
* - copy unselected values back from the original.
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index 340a7ae92ff..43492cd57af 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -912,74 +912,72 @@ static void edbm_bevel_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *col, *row;
- PointerRNA ptr, toolsettings_ptr;
+ PointerRNA toolsettings_ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- int profile_type = RNA_enum_get(&ptr, "profile_type");
- int offset_type = RNA_enum_get(&ptr, "offset_type");
- bool affect_type = RNA_enum_get(&ptr, "affect");
+ int profile_type = RNA_enum_get(op->ptr, "profile_type");
+ int offset_type = RNA_enum_get(op->ptr, "offset_type");
+ bool affect_type = RNA_enum_get(op->ptr, "affect");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "offset_type", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset_type", 0, NULL, ICON_NONE);
if (offset_type == BEVEL_AMT_PERCENT) {
- uiItemR(layout, &ptr, "offset_pct", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset_pct", 0, NULL, ICON_NONE);
}
else {
- uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE);
}
- uiItemR(layout, &ptr, "segments", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "segments", 0, NULL, ICON_NONE);
if (ELEM(profile_type, BEVEL_PROFILE_SUPERELLIPSE, BEVEL_PROFILE_CUSTOM)) {
uiItemR(layout,
- &ptr,
+ op->ptr,
"profile",
UI_ITEM_R_SLIDER,
(profile_type == BEVEL_PROFILE_SUPERELLIPSE) ? IFACE_("Shape") : IFACE_("Miter Shape"),
ICON_NONE);
}
- uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "material", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "harden_normals", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "clamp_overlap", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "loop_slide", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "harden_normals", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "clamp_overlap", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "loop_slide", 0, NULL, ICON_NONE);
col = uiLayoutColumnWithHeading(layout, true, IFACE_("Mark"));
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE);
- uiItemR(col, &ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE);
+ uiItemR(col, op->ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE);
+ uiItemR(col, op->ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE);
uiItemS(layout);
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE);
- uiItemR(col, &ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE);
- if (RNA_enum_get(&ptr, "miter_inner") == BEVEL_MITER_ARC) {
- uiItemR(col, &ptr, "spread", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE);
+ uiItemR(col, op->ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE);
+ if (RNA_enum_get(op->ptr, "miter_inner") == BEVEL_MITER_ARC) {
+ uiItemR(col, op->ptr, "spread", 0, NULL, ICON_NONE);
}
uiItemS(layout);
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE);
+ uiItemR(col, op->ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE);
- uiItemR(layout, &ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE);
+ uiItemR(layout, op->ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
if (profile_type == BEVEL_PROFILE_CUSTOM) {
/* Get an RNA pointer to ToolSettings to give to the curve profile template code. */
Scene *scene = CTX_data_scene(C);
diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c
index bc8c456889d..ea35d5a9e26 100644
--- a/source/blender/editors/mesh/editmesh_bisect.c
+++ b/source/blender/editors/mesh/editmesh_bisect.c
@@ -444,15 +444,17 @@ void MESH_OT_bisect(struct wmOperatorType *ot)
RNA_def_boolean(
ot->srna, "clear_outer", false, "Clear Outer", "Remove geometry in front of the plane");
- RNA_def_float(ot->srna,
- "threshold",
- 0.0001,
- 0.0,
- 10.0,
- "Axis Threshold",
- "Preserves the existing geometry along the cut plane",
- 0.00001,
- 0.1);
+ prop = RNA_def_float(ot->srna,
+ "threshold",
+ 0.0001,
+ 0.0,
+ 10.0,
+ "Axis Threshold",
+ "Preserves the existing geometry along the cut plane",
+ 0.00001,
+ 0.1);
+ /* Without higher precision, the default value displays as zero. */
+ RNA_def_property_ui_range(prop, 0.0, 10.0, 0.01, 5);
WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT);
diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
index feb6b5aaca9..38d530ba911 100644
--- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
+++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
@@ -468,6 +468,7 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt)
gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool;
gzgt->setup = gizmo_mesh_spin_init_setup;
+ gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
gzgt->refresh = gizmo_mesh_spin_init_refresh;
gzgt->message_subscribe = gizmo_mesh_spin_init_message_subscribe;
gzgt->draw_prepare = gizmo_mesh_spin_init_draw_prepare;
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 0e3cc22d358..d1f228e951a 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -255,27 +255,24 @@ static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *row;
- PointerRNA ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+ bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
if (!use_exact) {
- uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE);
}
}
@@ -421,27 +418,24 @@ static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *row;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+ bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_swap", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_self", 0, NULL, ICON_NONE);
if (!use_exact) {
- uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE);
}
}
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index df0ca6e3cae..ae824cad50b 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -74,7 +74,7 @@
/* detect isolated holes and fill them */
#define USE_NET_ISLAND_CONNECT
-#define KMAXDIST 10 /* max mouse distance from edge before not detecting it */
+#define KMAXDIST (10 * U.dpi_fac) /* max mouse distance from edge before not detecting it */
/* WARNING: knife float precision is fragile:
* be careful before making changes here see: (T43229, T42864, T42459, T41164).
@@ -146,6 +146,8 @@ typedef struct KnifePosData {
KnifeVert *vert;
KnifeEdge *edge;
BMFace *bmface;
+
+ /** When true, the cursor isn't over a face. */
bool is_space;
float mval[2]; /* mouse screen position (may be non-integral if snapped to something) */
@@ -196,10 +198,19 @@ typedef struct KnifeTool_OpData {
KnifePosData prev; /* last added cut (a line draws from the cursor to this) */
KnifePosData init; /* the first point in the cut-list, used for closing the loop */
- int totkedge, totkvert;
+ /** Number of knife edges `kedges`. */
+ int totkedge;
+ /** Number of knife vertices, `kverts`. */
+ int totkvert;
BLI_mempool *refs;
+ /**
+ * Use this instead of #Object.imat since it's calculated using #invert_m4_m4_safe_ortho
+ * to support objects with zero scale on a single axis.
+ */
+ float ob_imat[4][4];
+
float projmat[4][4];
float projmat_inv[4][4];
/* vector along view z axis (object space, normalized) */
@@ -243,8 +254,8 @@ enum {
KNF_MODAL_MIDPOINT_ON,
KNF_MODAL_MIDPOINT_OFF,
KNF_MODAL_NEW_CUT,
- KNF_MODEL_IGNORE_SNAP_ON,
- KNF_MODEL_IGNORE_SNAP_OFF,
+ KNF_MODAL_IGNORE_SNAP_ON,
+ KNF_MODAL_IGNORE_SNAP_OFF,
KNF_MODAL_ADD_CUT,
KNF_MODAL_ANGLE_SNAP_TOGGLE,
KNF_MODAL_CUT_THROUGH_TOGGLE,
@@ -252,19 +263,263 @@ enum {
KNF_MODAL_ADD_CUT_CLOSED,
};
-static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f);
+/* -------------------------------------------------------------------- */
+/** \name Drawing
+ * \{ */
-static void knife_input_ray_segment(KnifeTool_OpData *kcd,
- const float mval[2],
- const float ofs,
- float r_origin[3],
- float r_origin_ofs[3]);
+static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
+{
+ float v1[3], v2[3];
+ float planes[4][4];
+
+ planes_from_projmat(
+ (const float(*)[4])kcd->projmat, planes[2], planes[0], planes[3], planes[1], NULL, NULL);
+
+ /* ray-cast all planes */
+ {
+ float ray_dir[3];
+ float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}};
+ float lambda_best[2] = {-FLT_MAX, FLT_MAX};
+ int i;
+
+ /* we (sometimes) need the lines to be at the same depth before projecting */
+#if 0
+ sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage);
+#else
+ {
+ float curr_cage_adjust[3];
+ float co_depth[3];
-static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f);
+ copy_v3_v3(co_depth, kcd->prev.cage);
+ mul_m4_v3(kcd->ob->obmat, co_depth);
+ ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust);
+ mul_m4_v3(kcd->ob_imat, curr_cage_adjust);
+
+ sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
+ }
+#endif
-static void knifetool_free_bmbvh(KnifeTool_OpData *kcd);
+ for (i = 0; i < 4; i++) {
+ float ray_hit[3];
+ float lambda_test;
+ if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) {
+ madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test);
+ if (lambda_test < 0.0f) {
+ if (lambda_test > lambda_best[0]) {
+ copy_v3_v3(ray_hit_best[0], ray_hit);
+ lambda_best[0] = lambda_test;
+ }
+ }
+ else {
+ if (lambda_test < lambda_best[1]) {
+ copy_v3_v3(ray_hit_best[1], ray_hit);
+ lambda_best[1] = lambda_test;
+ }
+ }
+ }
+ }
+
+ copy_v3_v3(v1, ray_hit_best[0]);
+ copy_v3_v3(v2, ray_hit_best[1]);
+ }
+
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ immUniformThemeColor3(TH_TRANSFORM);
+ GPU_line_width(2.0);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, v1);
+ immVertex3fv(pos, v2);
+ immEnd();
+
+ immUnbindProgram();
+}
+
+/* modal loop selection drawing callback */
+static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
+{
+ const KnifeTool_OpData *kcd = arg;
+ GPU_depth_test(GPU_DEPTH_NONE);
+
+ GPU_matrix_push_projection();
+ GPU_polygon_offset(1.0f, 1.0f);
+
+ GPU_matrix_push();
+ GPU_matrix_mul(kcd->ob->obmat);
+
+ if (kcd->mode == MODE_DRAGGING && kcd->is_angle_snapping) {
+ knifetool_draw_angle_snapping(kcd);
+ }
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+
+ if (kcd->mode == MODE_DRAGGING) {
+ immUniformColor3ubv(kcd->colors.line);
+ GPU_line_width(2.0);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, kcd->prev.cage);
+ immVertex3fv(pos, kcd->curr.cage);
+ immEnd();
+ }
+
+ if (kcd->prev.vert) {
+ immUniformColor3ubv(kcd->colors.point);
+ GPU_point_size(11 * UI_DPI_FAC);
-static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event);
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, kcd->prev.cage);
+ immEnd();
+ }
+
+ if (kcd->prev.bmface || kcd->prev.edge) {
+ immUniformColor3ubv(kcd->colors.curpoint);
+ GPU_point_size(9 * UI_DPI_FAC);
+
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, kcd->prev.cage);
+ immEnd();
+ }
+
+ if (kcd->curr.vert) {
+ immUniformColor3ubv(kcd->colors.point);
+ GPU_point_size(11 * UI_DPI_FAC);
+
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, kcd->curr.cage);
+ immEnd();
+ }
+ else if (kcd->curr.edge) {
+ immUniformColor3ubv(kcd->colors.edge);
+ GPU_line_width(2.0);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, kcd->curr.edge->v1->cageco);
+ immVertex3fv(pos, kcd->curr.edge->v2->cageco);
+ immEnd();
+ }
+
+ if (kcd->curr.bmface || kcd->curr.edge) {
+ immUniformColor3ubv(kcd->colors.curpoint);
+ GPU_point_size(9 * UI_DPI_FAC);
+
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, kcd->curr.cage);
+ immEnd();
+ }
+
+ if (kcd->totlinehit > 0) {
+ KnifeLineHit *lh;
+ int i, snapped_verts_count, other_verts_count;
+ float fcol[4];
+
+ GPU_blend(GPU_BLEND_ALPHA);
+
+ GPUVertBuf *vert = GPU_vertbuf_create_with_format(format);
+ GPU_vertbuf_data_alloc(vert, kcd->totlinehit);
+
+ lh = kcd->linehits;
+ for (i = 0, snapped_verts_count = 0, other_verts_count = 0; i < kcd->totlinehit; i++, lh++) {
+ if (lh->v) {
+ GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, lh->cagehit);
+ }
+ else {
+ GPU_vertbuf_attr_set(vert, pos, kcd->totlinehit - 1 - other_verts_count++, lh->cagehit);
+ }
+ }
+
+ GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
+
+ /* draw any snapped verts first */
+ rgba_uchar_to_float(fcol, kcd->colors.point_a);
+ GPU_batch_uniform_4fv(batch, "color", fcol);
+ GPU_point_size(11 * UI_DPI_FAC);
+ if (snapped_verts_count > 0) {
+ GPU_batch_draw_range(batch, 0, snapped_verts_count);
+ }
+
+ /* now draw the rest */
+ rgba_uchar_to_float(fcol, kcd->colors.curpoint_a);
+ GPU_batch_uniform_4fv(batch, "color", fcol);
+ GPU_point_size(7 * UI_DPI_FAC);
+ if (other_verts_count > 0) {
+ GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count);
+ }
+
+ GPU_batch_discard(batch);
+
+ GPU_blend(GPU_BLEND_NONE);
+ }
+
+ if (kcd->totkedge > 0) {
+ BLI_mempool_iter iter;
+ KnifeEdge *kfe;
+
+ immUniformColor3ubv(kcd->colors.line);
+ GPU_line_width(1.0);
+
+ GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2);
+
+ BLI_mempool_iternew(kcd->kedges, &iter);
+ for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+ if (!kfe->is_cut) {
+ continue;
+ }
+
+ immVertex3fv(pos, kfe->v1->cageco);
+ immVertex3fv(pos, kfe->v2->cageco);
+ }
+
+ immEnd();
+
+ GPU_batch_draw(batch);
+ GPU_batch_discard(batch);
+ }
+
+ if (kcd->totkvert > 0) {
+ BLI_mempool_iter iter;
+ KnifeVert *kfv;
+
+ immUniformColor3ubv(kcd->colors.point);
+ GPU_point_size(5.0 * UI_DPI_FAC);
+
+ GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_POINTS, BLI_mempool_len(kcd->kverts));
+
+ BLI_mempool_iternew(kcd->kverts, &iter);
+ for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
+ if (!kfv->is_cut) {
+ continue;
+ }
+
+ immVertex3fv(pos, kfv->cageco);
+ }
+
+ immEnd();
+
+ GPU_batch_draw(batch);
+ GPU_batch_discard(batch);
+ }
+
+ immUnbindProgram();
+
+ GPU_matrix_pop();
+ GPU_matrix_pop_projection();
+
+ /* Reset default */
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Header
+ * \{ */
static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *kcd)
{
@@ -292,7 +547,7 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k
WM_MODALKEY(KNF_MODAL_NEW_CUT),
WM_MODALKEY(KNF_MODAL_MIDPOINT_ON),
WM_bool_as_string(kcd->snap_midpoints),
- WM_MODALKEY(KNF_MODEL_IGNORE_SNAP_ON),
+ WM_MODALKEY(KNF_MODAL_IGNORE_SNAP_ON),
WM_bool_as_string(kcd->ignore_edge_snapping),
WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE),
WM_bool_as_string(kcd->angle_snapping),
@@ -305,51 +560,160 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k
ED_workspace_status_text(C, header);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Utils
+ * \{ */
+
static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], float sco[2])
{
ED_view3d_project_float_v2_m4(kcd->region, co, sco, (float(*)[4])kcd->projmat);
}
-/* use when lambda is in screen-space */
-static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd,
- float r_co[3],
- const float v1[3],
- const float v2[3],
- float lambda_ss)
+static void knife_input_ray_segment(KnifeTool_OpData *kcd,
+ const float mval[2],
+ const float ofs,
+ float r_origin[3],
+ float r_origin_ofs[3])
{
- if (kcd->is_ortho) {
- interp_v3_v3v3(r_co, v1, v2, lambda_ss);
+ /* unproject to find view ray */
+ ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin);
+ ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs);
+
+ /* transform into object space */
+ mul_m4_v3(kcd->ob_imat, r_origin);
+ mul_m4_v3(kcd->ob_imat, r_origin_ofs);
+}
+
+static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f)
+{
+ bool v1_inside, v2_inside;
+ bool v1_inface, v2_inface;
+ BMLoop *l1, *l2;
+
+ if (!f || !v1 || !v2) {
+ return false;
}
- else {
- /* transform into screen-space, interp, then transform back */
- float v1_ss[3], v2_ss[3];
- mul_v3_project_m4_v3(v1_ss, (float(*)[4])kcd->projmat, v1);
- mul_v3_project_m4_v3(v2_ss, (float(*)[4])kcd->projmat, v2);
+ l1 = v1->v ? BM_face_vert_share_loop(f, v1->v) : NULL;
+ l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : NULL;
- interp_v3_v3v3(r_co, v1_ss, v2_ss, lambda_ss);
+ if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) {
+ /* boundary-case, always false to avoid edge-in-face checks below */
+ return false;
+ }
- mul_project_m4_v3((float(*)[4])kcd->projmat_inv, r_co);
+ /* find out if v1 and v2, if set, are part of the face */
+ v1_inface = (l1 != NULL);
+ v2_inface = (l2 != NULL);
+
+ /* BM_face_point_inside_test uses best-axis projection so this isn't most accurate test... */
+ v1_inside = v1_inface ? false : BM_face_point_inside_test(f, v1->co);
+ v2_inside = v2_inface ? false : BM_face_point_inside_test(f, v2->co);
+ if ((v1_inface && v2_inside) || (v2_inface && v1_inside) || (v1_inside && v2_inside)) {
+ return true;
}
+
+ if (v1_inface && v2_inface) {
+ float mid[3];
+ /* Can have case where v1 and v2 are on shared chain between two faces.
+ * BM_face_splits_check_legal does visibility and self-intersection tests,
+ * but it is expensive and maybe a bit buggy, so use a simple
+ * "is the midpoint in the face" test */
+ mid_v3_v3v3(mid, v1->co, v2->co);
+ return BM_face_point_inside_test(f, mid);
+ }
+ return false;
}
-static void knife_pos_data_clear(KnifePosData *kpd)
+static void knife_recalc_projmat(KnifeTool_OpData *kcd)
{
- zero_v3(kpd->co);
- zero_v3(kpd->cage);
- kpd->vert = NULL;
- kpd->edge = NULL;
- kpd->bmface = NULL;
- zero_v2(kpd->mval);
+ ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, kcd->projmat);
+ invert_m4_m4(kcd->projmat_inv, kcd->projmat);
+
+ invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat);
+ mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob_imat, kcd->vc.rv3d->viewinv[2]);
+ normalize_v3(kcd->proj_zaxis);
+
+ kcd->is_ortho = ED_view3d_clip_range_get(
+ kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, &kcd->clipsta, &kcd->clipend, true);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Knife Element Utils
+ *
+ * Currently only used in #knife_find_line_hits.
+ * \{ */
+
+static BMElem *bm_elem_from_knife_vert(KnifeVert *kfv, KnifeEdge **r_kfe)
+{
+ BMElem *ele_test;
+ KnifeEdge *kfe = NULL;
+
+ /* vert? */
+ ele_test = (BMElem *)kfv->v;
+
+ if (r_kfe || ele_test == NULL) {
+ if (kfv->v == NULL) {
+ Ref *ref;
+ for (ref = kfv->edges.first; ref; ref = ref->next) {
+ kfe = ref->ref;
+ if (kfe->e) {
+ if (r_kfe) {
+ *r_kfe = kfe;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /* edge? */
+ if (ele_test == NULL) {
+ if (kfe) {
+ ele_test = (BMElem *)kfe->e;
+ }
+ }
+
+ /* face? */
+ if (ele_test == NULL) {
+ if (BLI_listbase_is_single(&kfe->faces)) {
+ ele_test = ((Ref *)kfe->faces.first)->ref;
+ }
+ }
+
+ return ele_test;
+}
+
+static BMElem *bm_elem_from_knife_edge(KnifeEdge *kfe)
+{
+ BMElem *ele_test;
+
+ ele_test = (BMElem *)kfe->e;
+
+ if (ele_test == NULL) {
+ ele_test = (BMElem *)kfe->basef;
+ }
+
+ return ele_test;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Knife Element List Utils
+ * \{ */
+
static ListBase *knife_empty_list(KnifeTool_OpData *kcd)
{
- ListBase *lst;
+ ListBase *list;
- lst = BLI_memarena_alloc(kcd->arena, sizeof(ListBase));
- BLI_listbase_clear(lst);
- return lst;
+ list = BLI_memarena_alloc(kcd->arena, sizeof(ListBase));
+ BLI_listbase_clear(list);
+ return list;
}
static void knife_append_list(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
@@ -381,12 +745,6 @@ static void knife_append_list_no_dup(KnifeTool_OpData *kcd, ListBase *lst, void
}
}
-static KnifeEdge *new_knife_edge(KnifeTool_OpData *kcd)
-{
- kcd->totkedge++;
- return BLI_mempool_calloc(kcd->kedges);
-}
-
static void knife_add_to_vert_edges(KnifeTool_OpData *kcd, KnifeEdge *kfe)
{
knife_append_list(kcd, &kfe->v1->edges, kfe);
@@ -420,6 +778,12 @@ static BMFace *knife_find_common_face(ListBase *faces1, ListBase *faces2)
return NULL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Knife Element Creation
+ * \{ */
+
static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const float cageco[3])
{
KnifeVert *kfv = BLI_mempool_calloc(kcd->kverts);
@@ -432,6 +796,12 @@ static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const
return kfv;
}
+static KnifeEdge *new_knife_edge(KnifeTool_OpData *kcd)
+{
+ kcd->totkedge++;
+ return BLI_mempool_calloc(kcd->kedges);
+}
+
/* get a KnifeVert wrapper for an existing BMVert */
static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v)
{
@@ -484,92 +854,24 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e)
return kfe;
}
-/* Record the index in kcd->em->looptris of first looptri triple for a given face,
- * given an index for some triple in that array.
- * This assumes that all of the triangles for a given face are contiguous
- * in that array (as they are by the current tessellation routines).
- * Actually store index + 1 in the hash, because 0 looks like "no entry"
- * to hash lookup routine; will reverse this in the get routine.
- * Doing this lazily rather than all at once for all faces.
- */
-static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index)
-{
- int i;
-
- if (BLI_ghash_lookup(kcd->facetrimap, f)) {
- return;
- }
-
- BLI_assert(index >= 0 && index < kcd->em->tottri);
- BLI_assert(kcd->em->looptris[index][0]->f == f);
- for (i = index - 1; i >= 0; i--) {
- if (kcd->em->looptris[i][0]->f != f) {
- i++;
- break;
- }
- }
- if (i == -1) {
- i++;
- }
-
- BLI_ghash_insert(kcd->facetrimap, f, POINTER_FROM_INT(i + 1));
-}
-
-/* This should only be called for faces that have had a lowest face tri set by previous function */
-static int get_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f)
-{
- int ans;
-
- ans = POINTER_AS_INT(BLI_ghash_lookup(kcd->facetrimap, f));
- BLI_assert(ans != 0);
- return ans - 1;
-}
-
-/* User has just clicked for first time or first time after a restart (E key).
- * Copy the current position data into prev. */
-static void knife_start_cut(KnifeTool_OpData *kcd)
-{
- kcd->prev = kcd->curr;
- kcd->curr.is_space = 0; /*TODO: why do we do this? */
-
- if (kcd->prev.vert == NULL && kcd->prev.edge == NULL) {
- float origin[3], origin_ofs[3];
- float ofs_local[3];
-
- negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs);
- invert_m4_m4(kcd->ob->imat, kcd->ob->obmat);
- mul_m4_v3(kcd->ob->imat, ofs_local);
-
- knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
-
- if (!isect_line_plane_v3(kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->proj_zaxis)) {
- zero_v3(kcd->prev.cage);
- }
-
- copy_v3_v3(kcd->prev.co, kcd->prev.cage); /*TODO: do we need this? */
- copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
- copy_v3_v3(kcd->curr.co, kcd->prev.co);
- }
-}
-
static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f)
{
- ListBase *lst = BLI_ghash_lookup(kcd->kedgefacemap, f);
+ ListBase *list = BLI_ghash_lookup(kcd->kedgefacemap, f);
- if (!lst) {
+ if (!list) {
BMIter bmiter;
BMEdge *e;
- lst = knife_empty_list(kcd);
+ list = knife_empty_list(kcd);
BM_ITER_ELEM (e, &bmiter, f, BM_EDGES_OF_FACE) {
- knife_append_list(kcd, lst, get_bm_knife_edge(kcd, e));
+ knife_append_list(kcd, list, get_bm_knife_edge(kcd, e));
}
- BLI_ghash_insert(kcd->kedgefacemap, f, lst);
+ BLI_ghash_insert(kcd->kedgefacemap, f, list);
}
- return lst;
+ return list;
}
static void knife_edge_append_face(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMFace *f)
@@ -625,6 +927,38 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd,
return newkfe->v2;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cut/Hit Utils
+ * \{ */
+
+/* User has just clicked for first time or first time after a restart (E key).
+ * Copy the current position data into prev. */
+static void knife_start_cut(KnifeTool_OpData *kcd)
+{
+ kcd->prev = kcd->curr;
+ kcd->curr.is_space = 0; /*TODO: why do we do this? */
+
+ if (kcd->prev.vert == NULL && kcd->prev.edge == NULL) {
+ float origin[3], origin_ofs[3];
+ float ofs_local[3];
+
+ negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs);
+ mul_m4_v3(kcd->ob_imat, ofs_local);
+
+ knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
+
+ if (!isect_line_plane_v3(kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->proj_zaxis)) {
+ zero_v3(kcd->prev.cage);
+ }
+
+ copy_v3_v3(kcd->prev.co, kcd->prev.cage); /*TODO: do we need this? */
+ copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
+ copy_v3_v3(kcd->curr.co, kcd->prev.co);
+ }
+}
+
static void linehit_to_knifepos(KnifePosData *kpos, KnifeLineHit *lh)
{
kpos->bmface = lh->f;
@@ -750,13 +1084,13 @@ static void add_hit_to_facehits(KnifeTool_OpData *kcd,
BMFace *f,
KnifeLineHit *hit)
{
- ListBase *lst = BLI_ghash_lookup(facehits, f);
+ ListBase *list = BLI_ghash_lookup(facehits, f);
- if (!lst) {
- lst = knife_empty_list(kcd);
- BLI_ghash_insert(facehits, f, lst);
+ if (!list) {
+ list = knife_empty_list(kcd);
+ BLI_ghash_insert(facehits, f, list);
}
- knife_append_list_no_dup(kcd, lst, hit);
+ knife_append_list_no_dup(kcd, list, hit);
}
/**
@@ -879,6 +1213,228 @@ static void knife_cut_face(KnifeTool_OpData *kcd, BMFace *f, ListBase *hits)
}
}
+static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfedges)
+{
+ BMesh *bm = kcd->em->bm;
+ KnifeEdge *kfe;
+ Ref *ref;
+ int edge_array_len = BLI_listbase_count(kfedges);
+ int i;
+
+ BMEdge **edge_array = BLI_array_alloca(edge_array, edge_array_len);
+
+ /* point to knife edges we've created edges in, edge_array aligned */
+ KnifeEdge **kfe_array = BLI_array_alloca(kfe_array, edge_array_len);
+
+ BLI_assert(BLI_gset_len(kcd->edgenet.edge_visit) == 0);
+
+ i = 0;
+ for (ref = kfedges->first; ref; ref = ref->next) {
+ bool is_new_edge = false;
+ kfe = ref->ref;
+
+ if (kfe->e == NULL) {
+ if (kfe->v1->v && kfe->v2->v) {
+ kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v);
+ }
+ }
+
+ if (kfe->e) {
+ if (BM_edge_in_face(kfe->e, f)) {
+ /* shouldn't happen, but in this case - just ignore */
+ continue;
+ }
+ }
+ else {
+ if (kfe->v1->v == NULL) {
+ kfe->v1->v = BM_vert_create(bm, kfe->v1->co, NULL, 0);
+ }
+ if (kfe->v2->v == NULL) {
+ kfe->v2->v = BM_vert_create(bm, kfe->v2->co, NULL, 0);
+ }
+ BLI_assert(kfe->e == NULL);
+ kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, NULL, 0);
+ if (kfe->e) {
+ if (kcd->select_result || BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ BM_edge_select_set(bm, kfe->e, true);
+ }
+ is_new_edge = true;
+ }
+ }
+
+ BLI_assert(kfe->e);
+
+ if (BLI_gset_add(kcd->edgenet.edge_visit, kfe->e)) {
+ kfe_array[i] = is_new_edge ? kfe : 0;
+ edge_array[i] = kfe->e;
+ i += 1;
+ }
+ }
+
+ if (i) {
+ const int edge_array_len_orig = i;
+ edge_array_len = i;
+
+#ifdef USE_NET_ISLAND_CONNECT
+ uint edge_array_holes_len;
+ BMEdge **edge_array_holes;
+ if (BM_face_split_edgenet_connect_islands(bm,
+ f,
+ edge_array,
+ edge_array_len,
+ true,
+ kcd->edgenet.arena,
+ &edge_array_holes,
+ &edge_array_holes_len)) {
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ for (i = edge_array_len; i < edge_array_holes_len; i++) {
+ BM_edge_select_set(bm, edge_array_holes[i], true);
+ }
+ }
+
+ edge_array_len = edge_array_holes_len;
+ edge_array = edge_array_holes; /* owned by the arena */
+ }
+#endif
+
+ {
+ BMFace **face_arr = NULL;
+ int face_arr_len;
+
+ BM_face_split_edgenet(bm, f, edge_array, edge_array_len, &face_arr, &face_arr_len);
+
+ if (face_arr) {
+ MEM_freeN(face_arr);
+ }
+ }
+
+ /* remove dangling edges, not essential - but nice for users */
+ for (i = 0; i < edge_array_len_orig; i++) {
+ if (kfe_array[i]) {
+ if (BM_edge_is_wire(kfe_array[i]->e)) {
+ BM_edge_kill(bm, kfe_array[i]->e);
+ kfe_array[i]->e = NULL;
+ }
+ }
+ }
+
+#ifdef USE_NET_ISLAND_CONNECT
+ BLI_memarena_clear(kcd->edgenet.arena);
+#endif
+ }
+
+ BLI_gset_clear(kcd->edgenet.edge_visit, NULL);
+}
+
+static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
+{
+ const KnifeVert *cur_a = ((const Ref *)cur_a_p)->ref;
+ const KnifeVert *cur_b = ((const Ref *)cur_b_p)->ref;
+ const float *co = co_p;
+ const float a_sq = len_squared_v3v3(co, cur_a->co);
+ const float b_sq = len_squared_v3v3(co, cur_b->co);
+
+ if (a_sq < b_sq) {
+ return -1;
+ }
+ if (a_sq > b_sq) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges */
+static void knife_make_cuts(KnifeTool_OpData *kcd)
+{
+ BMesh *bm = kcd->em->bm;
+ KnifeEdge *kfe;
+ KnifeVert *kfv;
+ BMFace *f;
+ BMEdge *e, *enew;
+ ListBase *list;
+ Ref *ref;
+ float pct;
+ SmallHashIter hiter;
+ BLI_mempool_iter iter;
+ SmallHash fhash_, *fhash = &fhash_;
+ SmallHash ehash_, *ehash = &ehash_;
+
+ BLI_smallhash_init(fhash);
+ BLI_smallhash_init(ehash);
+
+ /* put list of cutting edges for a face into fhash, keyed by face */
+ BLI_mempool_iternew(kcd->kedges, &iter);
+ for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+
+ /* select edges that lie directly on the cut */
+ if (kcd->select_result) {
+ if (kfe->e && kfe->is_cut) {
+ BM_edge_select_set(bm, kfe->e, true);
+ }
+ }
+
+ f = kfe->basef;
+ if (!f || kfe->e) {
+ continue;
+ }
+ list = BLI_smallhash_lookup(fhash, (uintptr_t)f);
+ if (!list) {
+ list = knife_empty_list(kcd);
+ BLI_smallhash_insert(fhash, (uintptr_t)f, list);
+ }
+ knife_append_list(kcd, list, kfe);
+ }
+
+ /* put list of splitting vertices for an edge into ehash, keyed by edge */
+ BLI_mempool_iternew(kcd->kverts, &iter);
+ for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
+ if (kfv->v) {
+ continue; /* already have a BMVert */
+ }
+ for (ref = kfv->edges.first; ref; ref = ref->next) {
+ kfe = ref->ref;
+ e = kfe->e;
+ if (!e) {
+ continue;
+ }
+ list = BLI_smallhash_lookup(ehash, (uintptr_t)e);
+ if (!list) {
+ list = knife_empty_list(kcd);
+ BLI_smallhash_insert(ehash, (uintptr_t)e, list);
+ }
+ /* there can be more than one kfe in kfv's list with same e */
+ if (!find_ref(list, kfv)) {
+ knife_append_list(kcd, list, kfv);
+ }
+ }
+ }
+
+ /* split bmesh edges where needed */
+ for (list = BLI_smallhash_iternew(ehash, &hiter, (uintptr_t *)&e); list;
+ list = BLI_smallhash_iternext(&hiter, (uintptr_t *)&e)) {
+ BLI_listbase_sort_r(list, sort_verts_by_dist_cb, e->v1->co);
+
+ for (ref = list->first; ref; ref = ref->next) {
+ kfv = ref->ref;
+ pct = line_point_factor_v3(kfv->co, e->v1->co, e->v2->co);
+ kfv->v = BM_edge_split(bm, e, e->v1, &enew, pct);
+ }
+ }
+
+ if (kcd->only_select) {
+ EDBM_flag_disable_all(kcd->em, BM_ELEM_SELECT);
+ }
+
+ /* do cuts for each face */
+ for (list = BLI_smallhash_iternew(fhash, &hiter, (uintptr_t *)&f); list;
+ list = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) {
+ knife_make_face_cuts(kcd, f, list);
+ }
+
+ BLI_smallhash_release(fhash);
+ BLI_smallhash_release(ehash);
+}
+
/* User has just left-clicked after the first time.
* Add all knife cuts implied by line from prev to curr.
* If that line crossed edges then kcd->linehits will be non-NULL.
@@ -891,7 +1447,7 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
BMFace *f;
Ref *r;
GHashIterator giter;
- ListBase *lst;
+ ListBase *list;
prepare_linehits_for_cut(kcd);
if (kcd->totlinehit == 0) {
@@ -926,8 +1482,8 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
* the v and the kfe or f fields will be non-NULL. */
GHASH_ITER (giter, facehits) {
f = (BMFace *)BLI_ghashIterator_getKey(&giter);
- lst = (ListBase *)BLI_ghashIterator_getValue(&giter);
- knife_cut_face(kcd, f, lst);
+ list = (ListBase *)BLI_ghashIterator_getValue(&giter);
+ knife_cut_face(kcd, f, list);
}
/* set up for next cut */
@@ -960,267 +1516,51 @@ static void knife_finish_cut(KnifeTool_OpData *kcd)
}
}
-static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
-{
- float v1[3], v2[3];
- float planes[4][4];
-
- planes_from_projmat(
- (const float(*)[4])kcd->projmat, planes[2], planes[0], planes[3], planes[1], NULL, NULL);
-
- /* ray-cast all planes */
- {
- float ray_dir[3];
- float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}};
- float lambda_best[2] = {-FLT_MAX, FLT_MAX};
- int i;
-
- /* we (sometimes) need the lines to be at the same depth before projecting */
-#if 0
- sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage);
-#else
- {
- float curr_cage_adjust[3];
- float co_depth[3];
-
- copy_v3_v3(co_depth, kcd->prev.cage);
- mul_m4_v3(kcd->ob->obmat, co_depth);
- ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust);
- mul_m4_v3(kcd->ob->imat, curr_cage_adjust);
-
- sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
- }
-#endif
-
- for (i = 0; i < 4; i++) {
- float ray_hit[3];
- float lambda_test;
- if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) {
- madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test);
- if (lambda_test < 0.0f) {
- if (lambda_test > lambda_best[0]) {
- copy_v3_v3(ray_hit_best[0], ray_hit);
- lambda_best[0] = lambda_test;
- }
- }
- else {
- if (lambda_test < lambda_best[1]) {
- copy_v3_v3(ray_hit_best[1], ray_hit);
- lambda_best[1] = lambda_test;
- }
- }
- }
- }
-
- copy_v3_v3(v1, ray_hit_best[0]);
- copy_v3_v3(v2, ray_hit_best[1]);
- }
-
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- immUniformThemeColor3(TH_TRANSFORM);
- GPU_line_width(2.0);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex3fv(pos, v1);
- immVertex3fv(pos, v2);
- immEnd();
+/** \} */
- immUnbindProgram();
-}
-
-static void knife_init_colors(KnifeColors *colors)
-{
- /* possible BMESH_TODO: add explicit themes or calculate these by
- * figuring out contrasting colors with grid / edges / verts
- * a la UI_make_axis_color */
- UI_GetThemeColorType3ubv(TH_NURB_VLINE, SPACE_VIEW3D, colors->line);
- UI_GetThemeColorType3ubv(TH_NURB_ULINE, SPACE_VIEW3D, colors->edge);
- UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint);
- UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint_a);
- colors->curpoint_a[3] = 102;
- UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point);
- UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point_a);
- colors->point_a[3] = 102;
-}
+/* -------------------------------------------------------------------- */
+/** \name Screen Line Hits (#knife_find_line_hits)
+ * \{ */
-/* modal loop selection drawing callback */
-static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
+/* Record the index in kcd->em->looptris of first looptri triple for a given face,
+ * given an index for some triple in that array.
+ * This assumes that all of the triangles for a given face are contiguous
+ * in that array (as they are by the current tessellation routines).
+ * Actually store index + 1 in the hash, because 0 looks like "no entry"
+ * to hash lookup routine; will reverse this in the get routine.
+ * Doing this lazily rather than all at once for all faces.
+ */
+static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index)
{
- const KnifeTool_OpData *kcd = arg;
- GPU_depth_test(GPU_DEPTH_NONE);
-
- GPU_matrix_push_projection();
- GPU_polygon_offset(1.0f, 1.0f);
-
- GPU_matrix_push();
- GPU_matrix_mul(kcd->ob->obmat);
-
- if (kcd->mode == MODE_DRAGGING && kcd->is_angle_snapping) {
- knifetool_draw_angle_snapping(kcd);
- }
-
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
-
- if (kcd->mode == MODE_DRAGGING) {
- immUniformColor3ubv(kcd->colors.line);
- GPU_line_width(2.0);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex3fv(pos, kcd->prev.cage);
- immVertex3fv(pos, kcd->curr.cage);
- immEnd();
- }
-
- if (kcd->prev.vert) {
- immUniformColor3ubv(kcd->colors.point);
- GPU_point_size(11);
-
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, kcd->prev.cage);
- immEnd();
- }
-
- if (kcd->prev.bmface) {
- immUniformColor3ubv(kcd->colors.curpoint);
- GPU_point_size(9);
-
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, kcd->prev.cage);
- immEnd();
- }
-
- if (kcd->curr.edge) {
- immUniformColor3ubv(kcd->colors.edge);
- GPU_line_width(2.0);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex3fv(pos, kcd->curr.edge->v1->cageco);
- immVertex3fv(pos, kcd->curr.edge->v2->cageco);
- immEnd();
- }
- else if (kcd->curr.vert) {
- immUniformColor3ubv(kcd->colors.point);
- GPU_point_size(11);
-
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, kcd->curr.cage);
- immEnd();
- }
-
- if (kcd->curr.bmface) {
- immUniformColor3ubv(kcd->colors.curpoint);
- GPU_point_size(9);
-
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, kcd->curr.cage);
- immEnd();
- }
-
- if (kcd->totlinehit > 0) {
- KnifeLineHit *lh;
- int i, snapped_verts_count, other_verts_count;
- float fcol[4];
-
- GPU_blend(GPU_BLEND_ALPHA);
-
- GPUVertBuf *vert = GPU_vertbuf_create_with_format(format);
- GPU_vertbuf_data_alloc(vert, kcd->totlinehit);
-
- lh = kcd->linehits;
- for (i = 0, snapped_verts_count = 0, other_verts_count = 0; i < kcd->totlinehit; i++, lh++) {
- if (lh->v) {
- GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, lh->cagehit);
- }
- else {
- GPU_vertbuf_attr_set(vert, pos, kcd->totlinehit - 1 - other_verts_count++, lh->cagehit);
- }
- }
-
- GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
-
- /* draw any snapped verts first */
- rgba_uchar_to_float(fcol, kcd->colors.point_a);
- GPU_batch_uniform_4fv(batch, "color", fcol);
- GPU_point_size(11);
- if (snapped_verts_count > 0) {
- GPU_batch_draw_range(batch, 0, snapped_verts_count);
- }
-
- /* now draw the rest */
- rgba_uchar_to_float(fcol, kcd->colors.curpoint_a);
- GPU_batch_uniform_4fv(batch, "color", fcol);
- GPU_point_size(7);
- if (other_verts_count > 0) {
- GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count);
- }
-
- GPU_batch_discard(batch);
+ int i;
- GPU_blend(GPU_BLEND_NONE);
+ if (BLI_ghash_lookup(kcd->facetrimap, f)) {
+ return;
}
- if (kcd->totkedge > 0) {
- BLI_mempool_iter iter;
- KnifeEdge *kfe;
-
- immUniformColor3ubv(kcd->colors.line);
- GPU_line_width(1.0);
-
- GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2);
-
- BLI_mempool_iternew(kcd->kedges, &iter);
- for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
- if (!kfe->is_cut) {
- continue;
- }
-
- immVertex3fv(pos, kfe->v1->cageco);
- immVertex3fv(pos, kfe->v2->cageco);
+ BLI_assert(index >= 0 && index < kcd->em->tottri);
+ BLI_assert(kcd->em->looptris[index][0]->f == f);
+ for (i = index - 1; i >= 0; i--) {
+ if (kcd->em->looptris[i][0]->f != f) {
+ i++;
+ break;
}
-
- immEnd();
-
- GPU_batch_draw(batch);
- GPU_batch_discard(batch);
}
-
- if (kcd->totkvert > 0) {
- BLI_mempool_iter iter;
- KnifeVert *kfv;
-
- immUniformColor3ubv(kcd->colors.point);
- GPU_point_size(5.0);
-
- GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_POINTS, BLI_mempool_len(kcd->kverts));
-
- BLI_mempool_iternew(kcd->kverts, &iter);
- for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
- if (!kfv->is_cut) {
- continue;
- }
-
- immVertex3fv(pos, kfv->cageco);
- }
-
- immEnd();
-
- GPU_batch_draw(batch);
- GPU_batch_discard(batch);
+ if (i == -1) {
+ i++;
}
- immUnbindProgram();
+ BLI_ghash_insert(kcd->facetrimap, f, POINTER_FROM_INT(i + 1));
+}
- GPU_matrix_pop();
- GPU_matrix_pop_projection();
+/* This should only be called for faces that have had a lowest face tri set by previous function */
+static int get_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f)
+{
+ int ans;
- /* Reset default */
- GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ ans = POINTER_AS_INT(BLI_ghash_lookup(kcd->facetrimap, f));
+ BLI_assert(ans != 0);
+ return ans - 1;
}
/**
@@ -1246,7 +1586,7 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd,
float se1[2], se2[2];
float d, lambda;
BMLoop **tri;
- ListBase *lst;
+ ListBase *list;
Ref *ref;
KnifeEdge *kfe;
@@ -1281,8 +1621,8 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd,
}
interp_v3_v3v3v3_uv(hit_cageco, lv1, lv2, lv3, ray_tri_uv);
/* Now check that far enough away from verts and edges */
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
+ list = knife_get_face_kedges(kcd, f);
+ for (ref = list->first; ref; ref = ref->next) {
kfe = ref->ref;
knife_project_v2(kcd, kfe->v1->cageco, se1);
knife_project_v2(kcd, kfe->v2->cageco, se2);
@@ -1323,59 +1663,6 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd)
mid_v3_v3v3(kcd->ortho_extent_center, min, max);
}
-static BMElem *bm_elem_from_knife_vert(KnifeVert *kfv, KnifeEdge **r_kfe)
-{
- BMElem *ele_test;
- KnifeEdge *kfe = NULL;
-
- /* vert? */
- ele_test = (BMElem *)kfv->v;
-
- if (r_kfe || ele_test == NULL) {
- if (kfv->v == NULL) {
- Ref *ref;
- for (ref = kfv->edges.first; ref; ref = ref->next) {
- kfe = ref->ref;
- if (kfe->e) {
- if (r_kfe) {
- *r_kfe = kfe;
- }
- break;
- }
- }
- }
- }
-
- /* edge? */
- if (ele_test == NULL) {
- if (kfe) {
- ele_test = (BMElem *)kfe->e;
- }
- }
-
- /* face? */
- if (ele_test == NULL) {
- if (BLI_listbase_is_single(&kfe->faces)) {
- ele_test = ((Ref *)kfe->faces.first)->ref;
- }
- }
-
- return ele_test;
-}
-
-static BMElem *bm_elem_from_knife_edge(KnifeEdge *kfe)
-{
- BMElem *ele_test;
-
- ele_test = (BMElem *)kfe->e;
-
- if (ele_test == NULL) {
- ele_test = (BMElem *)kfe->basef;
- }
-
- return ele_test;
-}
-
/* Do edges e1 and e2 go between exactly the same coordinates? */
static bool coinciding_edges(BMEdge *e1, BMEdge *e2)
{
@@ -1458,9 +1745,9 @@ static bool point_is_visible(KnifeTool_OpData *kcd,
float view[3], p_ofs[3];
/* TODO: I think there's a simpler way to get the required raycast ray */
- ED_view3d_unproject(kcd->vc.region, s[0], s[1], 0.0f, view);
+ ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view);
- mul_m4_v3(kcd->ob->imat, view);
+ mul_m4_v3(kcd->ob_imat, view);
/* make p_ofs a little towards view, so ray doesn't hit p's face. */
sub_v3_v3(view, p);
@@ -1546,7 +1833,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BMFace *f;
KnifeEdge *kfe;
KnifeVert *v;
- ListBase *lst;
+ ListBase *list;
Ref *ref;
KnifeLineHit *linehits = NULL;
BLI_array_declare(linehits);
@@ -1591,10 +1878,10 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s1, v1, v3, true);
ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s2, v2, v4, true);
- mul_m4_v3(kcd->ob->imat, v1);
- mul_m4_v3(kcd->ob->imat, v2);
- mul_m4_v3(kcd->ob->imat, v3);
- mul_m4_v3(kcd->ob->imat, v4);
+ mul_m4_v3(kcd->ob_imat, v1);
+ mul_m4_v3(kcd->ob_imat, v2);
+ mul_m4_v3(kcd->ob_imat, v3);
+ mul_m4_v3(kcd->ob_imat, v4);
/* Numeric error, 'v1' -> 'v2', 'v2' -> 'v4'
* can end up being ~2000 units apart with an orthogonal perspective.
@@ -1650,8 +1937,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
/* don't care what the value is except that it is non-NULL, for iterator */
BLI_smallhash_insert(&faces, (uintptr_t)f, f);
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
+ list = knife_get_face_kedges(kcd, f);
+ for (ref = list->first; ref; ref = ref->next) {
kfe = ref->ref;
if (BLI_smallhash_haskey(&kfes, (uintptr_t)kfe)) {
continue;
@@ -1853,28 +2140,31 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_smallhash_release(&faces);
BLI_smallhash_release(&kfes);
BLI_smallhash_release(&kfvs);
- if (results) {
- MEM_freeN(results);
- }
+ MEM_freeN(results);
}
-static void knife_input_ray_segment(KnifeTool_OpData *kcd,
- const float mval[2],
- const float ofs,
- float r_origin[3],
- float r_origin_ofs[3])
-{
- /* unproject to find view ray */
- ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin);
- ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs);
+/** \} */
- /* transform into object space */
- invert_m4_m4(kcd->ob->imat, kcd->ob->obmat);
+/* -------------------------------------------------------------------- */
+/** \name KnifePosData Utils
+ * \{ */
- mul_m4_v3(kcd->ob->imat, r_origin);
- mul_m4_v3(kcd->ob->imat, r_origin_ofs);
+static void knife_pos_data_clear(KnifePosData *kpd)
+{
+ zero_v3(kpd->co);
+ zero_v3(kpd->cage);
+ kpd->vert = NULL;
+ kpd->edge = NULL;
+ kpd->bmface = NULL;
+ zero_v2(kpd->mval);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Snapping (#knife_snap_update_from_mval)
+ * \{ */
+
static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd,
float co[3],
float cageco[3],
@@ -1917,6 +2207,8 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd,
* of a true coordinate on the face.
* This just puts a point 1.0f in front of the view. */
add_v3_v3v3(co, origin, ray);
+ /* Use this value for the cage location too as it's used to find near edges/vertices. */
+ copy_v3_v3(cageco, co);
}
}
@@ -1926,64 +2218,119 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd,
/**
* Find the 2d screen space density of vertices within a radius.
* Used to scale snapping distance for picking edges/verts.
+ *
+ * Arguments `f` and `cageco` should be the result of a call to #knife_find_closest_face.
*/
-static int knife_sample_screen_density(KnifeTool_OpData *kcd, const float radius)
+static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd,
+ const float radius,
+ BMFace *f,
+ const float cageco[3])
{
- BMFace *f;
- bool is_space;
- float co[3], cageco[3], sco[2];
-
- BLI_assert(kcd->is_interactive == true);
-
- f = knife_find_closest_face(kcd, co, cageco, &is_space);
-
- if (f && !is_space) {
- const float radius_sq = radius * radius;
- ListBase *lst;
- Ref *ref;
- float dis_sq;
- int c = 0;
+ const float radius_sq = radius * radius;
+ ListBase *list;
+ Ref *ref;
+ float sco[2];
+ float dis_sq;
+ int c = 0;
- knife_project_v2(kcd, cageco, sco);
+ knife_project_v2(kcd, cageco, sco);
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
- KnifeEdge *kfe = ref->ref;
- int i;
+ list = knife_get_face_kedges(kcd, f);
+ for (ref = list->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+ int i;
- for (i = 0; i < 2; i++) {
- KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
- float kfv_sco[2];
+ for (i = 0; i < 2; i++) {
+ KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ float kfv_sco[2];
- knife_project_v2(kcd, kfv->cageco, kfv_sco);
+ knife_project_v2(kcd, kfv->cageco, kfv_sco);
- dis_sq = len_squared_v2v2(kfv_sco, sco);
- if (dis_sq < radius_sq) {
- if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
- if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) {
- c++;
- }
- }
- else {
+ dis_sq = len_squared_v2v2(kfv_sco, sco);
+ if (dis_sq < radius_sq) {
+ if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
+ if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) {
c++;
}
}
+ else {
+ c++;
+ }
}
}
-
- return c;
}
- return 0;
+ return c;
}
-/* returns snapping distance for edges/verts, scaled by the density of the
- * surrounding mesh (in screen space)*/
+/**
+ * \return the snapping distance for edges/verts, scaled by the density of the
+ * surrounding mesh (in screen space).
+ *
+ * \note Face values in `kcd->curr` must be up to date.
+ */
static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
{
- float density = (float)knife_sample_screen_density(kcd, maxsize * 2.0f);
+ BLI_assert(kcd->is_interactive == true);
+ int density = 0;
+
+ if (!kcd->curr.is_space) {
+ density = (float)knife_sample_screen_density_from_closest_face(
+ kcd, maxsize * 2.0f, kcd->curr.bmface, kcd->curr.cage);
+ }
+
+ return density ? min_ff(maxsize / ((float)density * 0.5f), maxsize) : maxsize;
+}
+
+/* Snap to edge in a specified angle.
+ * Returns 'lambda' calculated (in screen-space). */
+static bool knife_snap_edge_in_angle(KnifeTool_OpData *kcd,
+ const float sco[3],
+ const float kfv1_sco[2],
+ const float kfv2_sco[2],
+ float *r_dist_sq,
+ float *r_lambda)
+{
+ /* if snapping, check we're in bounds */
+ float sco_snap[2];
+ isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
+ float lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco);
+
+ /* be strict about angle-snapping within edge */
+ if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
+ return false;
+ }
- return min_ff(maxsize / (density * 0.5f), maxsize);
+ float dis_sq = len_squared_v2v2(sco, sco_snap);
+ if (dis_sq < *r_dist_sq) {
+ *r_dist_sq = dis_sq;
+ *r_lambda = lambda;
+ return true;
+ }
+ return false;
+}
+
+/* use when lambda is in screen-space */
+static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd,
+ float r_co[3],
+ const float v1[3],
+ const float v2[3],
+ float lambda_ss)
+{
+ if (kcd->is_ortho) {
+ interp_v3_v3v3(r_co, v1, v2, lambda_ss);
+ }
+ else {
+ /* transform into screen-space, interp, then transform back */
+ float v1_ss[3], v2_ss[3];
+
+ mul_v3_project_m4_v3(v1_ss, (float(*)[4])kcd->projmat, v1);
+ mul_v3_project_m4_v3(v2_ss, (float(*)[4])kcd->projmat, v2);
+
+ interp_v3_v3v3(r_co, v1_ss, v2_ss, lambda_ss);
+
+ mul_project_m4_v3((float(*)[4])kcd->projmat_inv, r_co);
+ }
}
/* p is closest point on edge to the mouse cursor */
@@ -2009,15 +2356,15 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
const float maxdist_sq = maxdist * maxdist;
KnifeEdge *cure = NULL;
float cur_cagep[3];
- ListBase *lst;
+ ListBase *list;
Ref *ref;
- float dis_sq, curdis_sq = FLT_MAX;
+ float dis_sq, curdis_sq = maxdist_sq;
knife_project_v2(kcd, cagep, sco);
/* look through all edges associated with this face */
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
+ list = knife_get_face_kedges(kcd, f);
+ for (ref = list->first; ref; ref = ref->next) {
KnifeEdge *kfe = ref->ref;
float kfv1_sco[2], kfv2_sco[2], test_cagep[3];
float lambda;
@@ -2028,27 +2375,14 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
/* check if we're close enough and calculate 'lambda' */
if (kcd->is_angle_snapping) {
- /* if snapping, check we're in bounds */
- float sco_snap[2];
- isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
- lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco);
-
- /* be strict about angle-snapping within edge */
- if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
- continue;
- }
-
- dis_sq = len_squared_v2v2(sco, sco_snap);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- /* we already have 'lambda' */
- }
- else {
+ dis_sq = curdis_sq;
+ if (!knife_snap_edge_in_angle(kcd, sco, kfv1_sco, kfv2_sco, &dis_sq, &lambda)) {
continue;
}
}
else {
dis_sq = dist_squared_to_line_segment_v2(sco, kfv1_sco, kfv2_sco);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ if (dis_sq < curdis_sq) {
lambda = line_point_factor_v2(sco, kfv1_sco, kfv2_sco);
}
else {
@@ -2071,36 +2405,31 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
copy_v3_v3(cur_cagep, test_cagep);
}
- if (cure) {
- if (!kcd->ignore_edge_snapping || !(cure->e)) {
- KnifeVert *edgesnap = NULL;
+ if (cure && !kcd->ignore_edge_snapping) {
+ KnifeVert *edgesnap = NULL;
- if (kcd->snap_midpoints) {
- mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
- mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
- }
- else {
- float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
- copy_v3_v3(cagep, cur_cagep);
- interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
- }
-
- /* update mouse coordinates to the snapped-to edge's screen coordinates
- * this is important for angle snap, which uses the previous mouse position */
- edgesnap = new_knife_vert(kcd, p, cagep);
- knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval);
+ if (kcd->snap_midpoints) {
+ mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
+ mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
}
else {
- return NULL;
+ float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
+ copy_v3_v3(cagep, cur_cagep);
+ interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
}
+
+ /* update mouse coordinates to the snapped-to edge's screen coordinates
+ * this is important for angle snap, which uses the previous mouse position */
+ edgesnap = new_knife_vert(kcd, p, cagep);
+ knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval);
}
return cure;
}
/* find a vertex near the mouse cursor, if it exists */
-static KnifeVert *knife_find_closest_vert_of_face(KnifeTool_OpData *kcd,
- BMFace *f,
+static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
+ KnifeEdge *kfe,
float p[3],
float cagep[3])
{
@@ -2118,60 +2447,48 @@ static KnifeVert *knife_find_closest_vert_of_face(KnifeTool_OpData *kcd,
}
const float maxdist_sq = maxdist * maxdist;
- ListBase *lst;
- Ref *ref;
KnifeVert *curv = NULL;
float cur_kfv_sco[2];
float dis_sq, curdis_sq = FLT_MAX;
knife_project_v2(kcd, cagep, sco);
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
- KnifeEdge *kfe = ref->ref;
- int i;
-
- for (i = 0; i < 2; i++) {
- KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
- float kfv_sco[2];
+ for (int i = 0; i < 2; i++) {
+ KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ float kfv_sco[2];
- knife_project_v2(kcd, kfv->cageco, kfv_sco);
+ knife_project_v2(kcd, kfv->cageco, kfv_sco);
- /* be strict about angle snapping, the vertex needs to be very close to the angle,
- * or we ignore */
- if (kcd->is_angle_snapping) {
- if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
- KNIFE_FLT_EPSBIG) {
- continue;
- }
+ /* be strict about angle snapping, the vertex needs to be very close to the angle,
+ * or we ignore */
+ if (kcd->is_angle_snapping) {
+ if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
+ KNIFE_FLT_EPSBIG) {
+ continue;
}
+ }
- dis_sq = len_squared_v2v2(kfv_sco, sco);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
- !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) {
- curv = kfv;
- curdis_sq = dis_sq;
- copy_v2_v2(cur_kfv_sco, kfv_sco);
- }
+ dis_sq = len_squared_v2v2(kfv_sco, sco);
+ if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
+ !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) {
+ curv = kfv;
+ curdis_sq = dis_sq;
+ copy_v2_v2(cur_kfv_sco, kfv_sco);
}
}
}
- if (!kcd->ignore_vert_snapping || !(curv && curv->v)) {
- if (curv) {
- copy_v3_v3(p, curv->co);
- copy_v3_v3(cagep, curv->cageco);
-
- /* update mouse coordinates to the snapped-to vertex's screen coordinates
- * this is important for angle snap, which uses the previous mouse position */
- copy_v2_v2(kcd->curr.mval, cur_kfv_sco);
- }
+ if (curv && !kcd->ignore_vert_snapping) {
+ copy_v3_v3(p, curv->co);
+ copy_v3_v3(cagep, curv->cageco);
- return curv;
+ /* update mouse coordinates to the snapped-to vertex's screen coordinates
+ * this is important for angle snap, which uses the previous mouse position */
+ copy_v2_v2(kcd->curr.mval, cur_kfv_sco);
}
- return NULL;
+ return curv;
}
/**
@@ -2215,7 +2532,15 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd)
return true;
}
-static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
+/**
+ * \return true when `kcd->curr.co` & `kcd->curr.cage` are set.
+ *
+ * In this case `is_space` is nearly always false.
+ * There are some situations when vertex or edge can be snapped to, when `is_space` is true.
+ * In this case the selection-buffer is used to select the face,
+ * then the closest `vert` or `edge` is set, and those will enable `is_co_set`.
+ */
+static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_pos_data_clear(&kcd->curr);
copy_v2_v2(kcd->curr.mval, mval);
@@ -2230,344 +2555,143 @@ static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[
kcd->is_angle_snapping = false;
}
- kcd->curr.bmface = knife_find_closest_face(
- kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space);
-
- if (kcd->curr.bmface) {
- kcd->curr.vert = knife_find_closest_vert_of_face(
- kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage);
+ {
+ kcd->curr.bmface = knife_find_closest_face(
+ kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space);
- if (!kcd->curr.vert &&
- /* no edge snapping while dragging (edges are too sticky when cuts are immediate) */
- !kcd->is_drag_hold) {
+ if (kcd->curr.bmface) {
kcd->curr.edge = knife_find_closest_edge_of_face(
kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage);
}
- }
-}
-/* update active knife edge/vert pointers */
-static int knife_update_active(KnifeTool_OpData *kcd)
-{
- knife_snap_update_from_mval(kcd, kcd->mval);
+ if (kcd->curr.edge) {
+ kcd->curr.vert = knife_find_closest_vert_of_edge(
+ kcd, kcd->curr.edge, kcd->curr.co, kcd->curr.cage);
- /* if no hits are found this would normally default to (0, 0, 0) so instead
- * get a point at the mouse ray closest to the previous point.
- * Note that drawing lines in `free-space` isn't properly supported
- * but there's no guarantee (0, 0, 0) has any geometry either - campbell */
- if (kcd->curr.vert == NULL && kcd->curr.edge == NULL && kcd->curr.bmface == NULL) {
- float origin[3];
- float origin_ofs[3];
-
- knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
-
- if (!isect_line_plane_v3(
- kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->proj_zaxis)) {
- copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
-
- /* should never fail! */
- BLI_assert(0);
+ if (kcd->ignore_edge_snapping) {
+ kcd->curr.edge = NULL;
+ }
}
}
- if (kcd->mode == MODE_DRAGGING) {
- knife_find_line_hits(kcd);
- }
- return 1;
+ return kcd->curr.vert || kcd->curr.edge || (kcd->curr.bmface && !kcd->curr.is_space);
}
-static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
-{
- const KnifeVert *cur_a = ((const Ref *)cur_a_p)->ref;
- const KnifeVert *cur_b = ((const Ref *)cur_b_p)->ref;
- const float *co = co_p;
- const float a_sq = len_squared_v3v3(co, cur_a->co);
- const float b_sq = len_squared_v3v3(co, cur_b->co);
+/** \} */
- if (a_sq < b_sq) {
- return -1;
- }
- if (a_sq > b_sq) {
- return 1;
- }
- return 0;
-}
+/* -------------------------------------------------------------------- */
+/** \name #KnifeTool_OpData (#op->customdata) Init and Free
+ * \{ */
-static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f)
+static void knifetool_init_bmbvh(KnifeTool_OpData *kcd)
{
- bool v1_inside, v2_inside;
- bool v1_inface, v2_inface;
- BMLoop *l1, *l2;
-
- if (!f || !v1 || !v2) {
- return false;
- }
+ BM_mesh_elem_index_ensure(kcd->em->bm, BM_VERT);
- l1 = v1->v ? BM_face_vert_share_loop(f, v1->v) : NULL;
- l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : NULL;
+ Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id);
+ Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->ob->id);
+ BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
- if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) {
- /* boundary-case, always false to avoid edge-in-face checks below */
- return false;
- }
+ kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc(
+ kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL);
- /* find out if v1 and v2, if set, are part of the face */
- v1_inface = (l1 != NULL);
- v2_inface = (l2 != NULL);
+ kcd->bmbvh = BKE_bmbvh_new_from_editmesh(
+ kcd->em,
+ BMBVH_RETURN_ORIG |
+ ((kcd->only_select && kcd->cut_through) ? BMBVH_RESPECT_SELECT : BMBVH_RESPECT_HIDDEN),
+ kcd->cagecos,
+ false);
+}
- /* BM_face_point_inside_test uses best-axis projection so this isn't most accurate test... */
- v1_inside = v1_inface ? false : BM_face_point_inside_test(f, v1->co);
- v2_inside = v2_inface ? false : BM_face_point_inside_test(f, v2->co);
- if ((v1_inface && v2_inside) || (v2_inface && v1_inside) || (v1_inside && v2_inside)) {
- return true;
+static void knifetool_free_bmbvh(KnifeTool_OpData *kcd)
+{
+ if (kcd->bmbvh) {
+ BKE_bmbvh_free(kcd->bmbvh);
+ kcd->bmbvh = NULL;
}
- if (v1_inface && v2_inface) {
- float mid[3];
- /* Can have case where v1 and v2 are on shared chain between two faces.
- * BM_face_splits_check_legal does visibility and self-intersection tests,
- * but it is expensive and maybe a bit buggy, so use a simple
- * "is the midpoint in the face" test */
- mid_v3_v3v3(mid, v1->co, v2->co);
- return BM_face_point_inside_test(f, mid);
+ if (kcd->cagecos) {
+ MEM_freeN((void *)kcd->cagecos);
+ kcd->cagecos = NULL;
}
- return false;
}
-static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfedges)
+static void knife_init_colors(KnifeColors *colors)
{
- BMesh *bm = kcd->em->bm;
- KnifeEdge *kfe;
- Ref *ref;
- int edge_array_len = BLI_listbase_count(kfedges);
- int i;
-
- BMEdge **edge_array = BLI_array_alloca(edge_array, edge_array_len);
-
- /* point to knife edges we've created edges in, edge_array aligned */
- KnifeEdge **kfe_array = BLI_array_alloca(kfe_array, edge_array_len);
-
- BLI_assert(BLI_gset_len(kcd->edgenet.edge_visit) == 0);
-
- i = 0;
- for (ref = kfedges->first; ref; ref = ref->next) {
- bool is_new_edge = false;
- kfe = ref->ref;
-
- if (kfe->e == NULL) {
- if (kfe->v1->v && kfe->v2->v) {
- kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v);
- }
- }
-
- if (kfe->e) {
- if (BM_edge_in_face(kfe->e, f)) {
- /* shouldn't happen, but in this case - just ignore */
- continue;
- }
- }
- else {
- if (kfe->v1->v == NULL) {
- kfe->v1->v = BM_vert_create(bm, kfe->v1->co, NULL, 0);
- }
- if (kfe->v2->v == NULL) {
- kfe->v2->v = BM_vert_create(bm, kfe->v2->co, NULL, 0);
- }
- BLI_assert(kfe->e == NULL);
- kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, NULL, 0);
- if (kfe->e) {
- if (kcd->select_result || BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- BM_edge_select_set(bm, kfe->e, true);
- }
- is_new_edge = true;
- }
- }
-
- BLI_assert(kfe->e);
-
- if (BLI_gset_add(kcd->edgenet.edge_visit, kfe->e)) {
- kfe_array[i] = is_new_edge ? kfe : 0;
- edge_array[i] = kfe->e;
- i += 1;
- }
- }
+ /* possible BMESH_TODO: add explicit themes or calculate these by
+ * figuring out contrasting colors with grid / edges / verts
+ * a la UI_make_axis_color */
+ UI_GetThemeColorType3ubv(TH_NURB_VLINE, SPACE_VIEW3D, colors->line);
+ UI_GetThemeColorType3ubv(TH_NURB_ULINE, SPACE_VIEW3D, colors->edge);
+ UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint);
+ UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint_a);
+ colors->curpoint_a[3] = 102;
+ UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point);
+ UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point_a);
+ colors->point_a[3] = 102;
+}
- if (i) {
- const int edge_array_len_orig = i;
- edge_array_len = i;
+/* called when modal loop selection gets set up... */
+static void knifetool_init(bContext *C,
+ KnifeTool_OpData *kcd,
+ const bool only_select,
+ const bool cut_through,
+ const bool is_interactive)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *obedit = CTX_data_edit_object(C);
-#ifdef USE_NET_ISLAND_CONNECT
- uint edge_array_holes_len;
- BMEdge **edge_array_holes;
- if (BM_face_split_edgenet_connect_islands(bm,
- f,
- edge_array,
- edge_array_len,
- true,
- kcd->edgenet.arena,
- &edge_array_holes,
- &edge_array_holes_len)) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- for (i = edge_array_len; i < edge_array_holes_len; i++) {
- BM_edge_select_set(bm, edge_array_holes[i], true);
- }
- }
+ /* assign the drawing handle for drawing preview line... */
+ kcd->scene = scene;
+ kcd->ob = obedit;
+ kcd->region = CTX_wm_region(C);
- edge_array_len = edge_array_holes_len;
- edge_array = edge_array_holes; /* owned by the arena */
- }
-#endif
+ invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat);
- {
- BMFace **face_arr = NULL;
- int face_arr_len;
+ em_setup_viewcontext(C, &kcd->vc);
- BM_face_split_edgenet(bm, f, edge_array, edge_array_len, &face_arr, &face_arr_len);
+ kcd->em = BKE_editmesh_from_object(kcd->ob);
- if (face_arr) {
- MEM_freeN(face_arr);
- }
- }
+ /* cut all the way through the mesh if use_occlude_geometry button not pushed */
+ kcd->is_interactive = is_interactive;
+ kcd->cut_through = cut_through;
+ kcd->only_select = only_select;
- /* remove dangling edges, not essential - but nice for users */
- for (i = 0; i < edge_array_len_orig; i++) {
- if (kfe_array[i]) {
- if (BM_edge_is_wire(kfe_array[i]->e)) {
- BM_edge_kill(bm, kfe_array[i]->e);
- kfe_array[i]->e = NULL;
- }
- }
- }
+ knifetool_init_bmbvh(kcd);
+ kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife");
#ifdef USE_NET_ISLAND_CONNECT
- BLI_memarena_clear(kcd->edgenet.arena);
+ kcd->edgenet.arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), __func__);
#endif
- }
-
- BLI_gset_clear(kcd->edgenet.edge_visit, NULL);
-}
-
-/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges */
-static void knife_make_cuts(KnifeTool_OpData *kcd)
-{
- BMesh *bm = kcd->em->bm;
- KnifeEdge *kfe;
- KnifeVert *kfv;
- BMFace *f;
- BMEdge *e, *enew;
- ListBase *lst;
- Ref *ref;
- float pct;
- SmallHashIter hiter;
- BLI_mempool_iter iter;
- SmallHash fhash_, *fhash = &fhash_;
- SmallHash ehash_, *ehash = &ehash_;
+ kcd->edgenet.edge_visit = BLI_gset_ptr_new(__func__);
- BLI_smallhash_init(fhash);
- BLI_smallhash_init(ehash);
+ kcd->vthresh = KMAXDIST - 1;
+ kcd->ethresh = KMAXDIST;
- /* put list of cutting edges for a face into fhash, keyed by face */
- BLI_mempool_iternew(kcd->kedges, &iter);
- for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+ knife_recalc_projmat(kcd);
- /* select edges that lie directly on the cut */
- if (kcd->select_result) {
- if (kfe->e && kfe->is_cut) {
- BM_edge_select_set(bm, kfe->e, true);
- }
- }
+ ED_region_tag_redraw(kcd->region);
- f = kfe->basef;
- if (!f || kfe->e) {
- continue;
- }
- lst = BLI_smallhash_lookup(fhash, (uintptr_t)f);
- if (!lst) {
- lst = knife_empty_list(kcd);
- BLI_smallhash_insert(fhash, (uintptr_t)f, lst);
- }
- knife_append_list(kcd, lst, kfe);
- }
+ kcd->refs = BLI_mempool_create(sizeof(Ref), 0, 2048, 0);
+ kcd->kverts = BLI_mempool_create(sizeof(KnifeVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
+ kcd->kedges = BLI_mempool_create(sizeof(KnifeEdge), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
- /* put list of splitting vertices for an edge into ehash, keyed by edge */
- BLI_mempool_iternew(kcd->kverts, &iter);
- for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
- if (kfv->v) {
- continue; /* already have a BMVert */
- }
- for (ref = kfv->edges.first; ref; ref = ref->next) {
- kfe = ref->ref;
- e = kfe->e;
- if (!e) {
- continue;
- }
- lst = BLI_smallhash_lookup(ehash, (uintptr_t)e);
- if (!lst) {
- lst = knife_empty_list(kcd);
- BLI_smallhash_insert(ehash, (uintptr_t)e, lst);
- }
- /* there can be more than one kfe in kfv's list with same e */
- if (!find_ref(lst, kfv)) {
- knife_append_list(kcd, lst, kfv);
- }
- }
- }
+ kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap");
+ kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap");
+ kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap");
+ kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap");
- /* split bmesh edges where needed */
- for (lst = BLI_smallhash_iternew(ehash, &hiter, (uintptr_t *)&e); lst;
- lst = BLI_smallhash_iternext(&hiter, (uintptr_t *)&e)) {
- BLI_listbase_sort_r(lst, sort_verts_by_dist_cb, e->v1->co);
+ /* can't usefully select resulting edges in face mode */
+ kcd->select_result = (kcd->em->selectmode != SCE_SELECT_FACE);
- for (ref = lst->first; ref; ref = ref->next) {
- kfv = ref->ref;
- pct = line_point_factor_v3(kfv->co, e->v1->co, e->v2->co);
- kfv->v = BM_edge_split(bm, e, e->v1, &enew, pct);
- }
- }
+ knife_pos_data_clear(&kcd->curr);
+ knife_pos_data_clear(&kcd->prev);
- if (kcd->only_select) {
- EDBM_flag_disable_all(kcd->em, BM_ELEM_SELECT);
- }
+ if (is_interactive) {
+ kcd->draw_handle = ED_region_draw_cb_activate(
+ kcd->region->type, knifetool_draw, kcd, REGION_DRAW_POST_VIEW);
- /* do cuts for each face */
- for (lst = BLI_smallhash_iternew(fhash, &hiter, (uintptr_t *)&f); lst;
- lst = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) {
- knife_make_face_cuts(kcd, f, lst);
+ knife_init_colors(&kcd->colors);
}
-
- BLI_smallhash_release(fhash);
- BLI_smallhash_release(ehash);
-}
-
-/* called on tool confirmation */
-static void knifetool_finish_ex(KnifeTool_OpData *kcd)
-{
- knife_make_cuts(kcd);
-
- EDBM_selectmode_flush(kcd->em);
- EDBM_mesh_normals_update(kcd->em);
- EDBM_update_generic(kcd->ob->data, true, true);
-
- /* Re-tessellating makes this invalid, don't use again by accident. */
- knifetool_free_bmbvh(kcd);
-}
-static void knifetool_finish(wmOperator *op)
-{
- KnifeTool_OpData *kcd = op->customdata;
- knifetool_finish_ex(kcd);
-}
-
-static void knife_recalc_projmat(KnifeTool_OpData *kcd)
-{
- invert_m4_m4(kcd->ob->imat, kcd->ob->obmat);
- ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, kcd->projmat);
- invert_m4_m4(kcd->projmat_inv, kcd->projmat);
-
- mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob->imat, kcd->vc.rv3d->viewinv[2]);
- normalize_v3(kcd->proj_zaxis);
-
- kcd->is_ortho = ED_view3d_clip_range_get(
- kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, &kcd->clipsta, &kcd->clipend, true);
}
/* called when modal loop selection is done... */
@@ -2619,6 +2743,40 @@ static void knifetool_exit(bContext *C, wmOperator *op)
op->customdata = NULL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mouse-Moving Event Updates
+ * \{ */
+
+/* update active knife edge/vert pointers */
+static int knife_update_active(KnifeTool_OpData *kcd)
+{
+ /* if no hits are found this would normally default to (0, 0, 0) so instead
+ * get a point at the mouse ray closest to the previous point.
+ * Note that drawing lines in `free-space` isn't properly supported
+ * but there's no guarantee (0, 0, 0) has any geometry either - campbell */
+ if (!knife_snap_update_from_mval(kcd, kcd->mval)) {
+ float origin[3];
+ float origin_ofs[3];
+
+ knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
+
+ if (!isect_line_plane_v3(
+ kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->proj_zaxis)) {
+ copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
+
+ /* should never fail! */
+ BLI_assert(0);
+ }
+ }
+
+ if (kcd->mode == MODE_DRAGGING) {
+ knife_find_line_hits(kcd);
+ }
+ return 1;
+}
+
static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_recalc_projmat(kcd);
@@ -2635,99 +2793,36 @@ static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2])
knifetool_update_mval(kcd, mval);
}
-static void knifetool_init_bmbvh(KnifeTool_OpData *kcd)
-{
- BM_mesh_elem_index_ensure(kcd->em->bm, BM_VERT);
+/** \} */
- Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id);
- Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->ob->id);
- BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
-
- kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc(
- kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL);
-
- kcd->bmbvh = BKE_bmbvh_new_from_editmesh(
- kcd->em,
- BMBVH_RETURN_ORIG |
- ((kcd->only_select && kcd->cut_through) ? BMBVH_RESPECT_SELECT : BMBVH_RESPECT_HIDDEN),
- kcd->cagecos,
- false);
-}
+/* -------------------------------------------------------------------- */
+/** \name Finalization
+ * \{ */
-static void knifetool_free_bmbvh(KnifeTool_OpData *kcd)
+/* called on tool confirmation */
+static void knifetool_finish_ex(KnifeTool_OpData *kcd)
{
- if (kcd->bmbvh) {
- BKE_bmbvh_free(kcd->bmbvh);
- kcd->bmbvh = NULL;
- }
+ knife_make_cuts(kcd);
- if (kcd->cagecos) {
- MEM_freeN((void *)kcd->cagecos);
- kcd->cagecos = NULL;
- }
+ EDBM_selectmode_flush(kcd->em);
+ EDBM_mesh_normals_update(kcd->em);
+ EDBM_update_generic(kcd->ob->data, true, true);
+
+ /* Re-tessellating makes this invalid, don't use again by accident. */
+ knifetool_free_bmbvh(kcd);
}
-/* called when modal loop selection gets set up... */
-static void knifetool_init(bContext *C,
- KnifeTool_OpData *kcd,
- const bool only_select,
- const bool cut_through,
- const bool is_interactive)
+static void knifetool_finish(wmOperator *op)
{
- Scene *scene = CTX_data_scene(C);
- Object *obedit = CTX_data_edit_object(C);
-
- /* assign the drawing handle for drawing preview line... */
- kcd->scene = scene;
- kcd->ob = obedit;
- kcd->region = CTX_wm_region(C);
-
- em_setup_viewcontext(C, &kcd->vc);
-
- kcd->em = BKE_editmesh_from_object(kcd->ob);
-
- /* cut all the way through the mesh if use_occlude_geometry button not pushed */
- kcd->is_interactive = is_interactive;
- kcd->cut_through = cut_through;
- kcd->only_select = only_select;
-
- knifetool_init_bmbvh(kcd);
-
- kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife");
-#ifdef USE_NET_ISLAND_CONNECT
- kcd->edgenet.arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), __func__);
-#endif
- kcd->edgenet.edge_visit = BLI_gset_ptr_new(__func__);
-
- kcd->vthresh = KMAXDIST - 1;
- kcd->ethresh = KMAXDIST;
-
- knife_recalc_projmat(kcd);
-
- ED_region_tag_redraw(kcd->region);
-
- kcd->refs = BLI_mempool_create(sizeof(Ref), 0, 2048, 0);
- kcd->kverts = BLI_mempool_create(sizeof(KnifeVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
- kcd->kedges = BLI_mempool_create(sizeof(KnifeEdge), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
-
- kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap");
- kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap");
- kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap");
- kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap");
-
- /* can't usefully select resulting edges in face mode */
- kcd->select_result = (kcd->em->selectmode != SCE_SELECT_FACE);
-
- knife_pos_data_clear(&kcd->curr);
- knife_pos_data_clear(&kcd->prev);
+ KnifeTool_OpData *kcd = op->customdata;
+ knifetool_finish_ex(kcd);
+}
- if (is_interactive) {
- kcd->draw_handle = ED_region_draw_cb_activate(
- kcd->region->type, knifetool_draw, kcd, REGION_DRAW_POST_VIEW);
+/** \} */
- knife_init_colors(&kcd->colors);
- }
-}
+/* -------------------------------------------------------------------- */
+/** \name Operator (#MESH_OT_knife_tool)
+ * \{ */
static void knifetool_cancel(bContext *C, wmOperator *op)
{
@@ -2735,53 +2830,6 @@ static void knifetool_cancel(bContext *C, wmOperator *op)
knifetool_exit(C, op);
}
-static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
- const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry");
- const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
-
- KnifeTool_OpData *kcd;
-
- if (only_select) {
- Object *obedit = CTX_data_edit_object(C);
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
- if (em->bm->totfacesel == 0) {
- BKE_report(op->reports, RPT_ERROR, "Selected faces required");
- return OPERATOR_CANCELLED;
- }
- }
-
- /* alloc new customdata */
- kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__);
-
- knifetool_init(C, kcd, only_select, cut_through, true);
-
- op->flag |= OP_IS_MODAL_CURSOR_REGION;
-
- /* add a modal handler for this operator - handles loop selection */
- WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE);
- WM_event_add_modal_handler(C, op);
-
- knifetool_update_mval_i(kcd, event->mval);
-
- if (wait_for_input == false) {
- /* Avoid copy-paste logic. */
- wmEvent event_modal = {
- .prevval = KM_NOTHING,
- .type = EVT_MODAL_MAP,
- .val = KNF_MODAL_ADD_CUT,
- };
- int ret = knifetool_modal(C, op, &event_modal);
- BLI_assert(ret == OPERATOR_RUNNING_MODAL);
- UNUSED_VARS_NDEBUG(ret);
- }
-
- knife_update_header(C, op, kcd);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
{
static const EnumPropertyItem modal_items[] = {
@@ -2789,8 +2837,8 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
{KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
{KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap to Midpoints On", ""},
{KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap to Midpoints Off", ""},
- {KNF_MODEL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""},
- {KNF_MODEL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""},
+ {KNF_MODAL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""},
+ {KNF_MODAL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""},
{KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""},
{KNF_MODAL_CUT_THROUGH_TOGGLE, "CUT_THROUGH_TOGGLE", 0, "Toggle Cut Through", ""},
{KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""},
@@ -2873,13 +2921,13 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_region_tag_redraw(kcd->region);
do_refresh = true;
break;
- case KNF_MODEL_IGNORE_SNAP_ON:
+ case KNF_MODAL_IGNORE_SNAP_ON:
ED_region_tag_redraw(kcd->region);
kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = true;
knife_update_header(C, op, kcd);
do_refresh = true;
break;
- case KNF_MODEL_IGNORE_SNAP_OFF:
+ case KNF_MODAL_IGNORE_SNAP_OFF:
ED_region_tag_redraw(kcd->region);
kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = false;
knife_update_header(C, op, kcd);
@@ -2917,10 +2965,13 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* freehand drawing is incompatible with cut-through */
if (kcd->cut_through == false) {
kcd->is_drag_hold = true;
+ /* No edge snapping while dragging (edges are too sticky when cuts are immediate). */
+ kcd->ignore_edge_snapping = true;
}
}
else {
kcd->is_drag_hold = false;
+ kcd->ignore_edge_snapping = false;
/* needed because the last face 'hit' is ignored when dragging */
knifetool_update_mval(kcd, kcd->curr.mval);
@@ -3006,6 +3057,53 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
+static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
+ const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry");
+ const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
+
+ KnifeTool_OpData *kcd;
+
+ if (only_select) {
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ if (em->bm->totfacesel == 0) {
+ BKE_report(op->reports, RPT_ERROR, "Selected faces required");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ /* alloc new customdata */
+ kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__);
+
+ knifetool_init(C, kcd, only_select, cut_through, true);
+
+ op->flag |= OP_IS_MODAL_CURSOR_REGION;
+
+ /* add a modal handler for this operator - handles loop selection */
+ WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE);
+ WM_event_add_modal_handler(C, op);
+
+ knifetool_update_mval_i(kcd, event->mval);
+
+ if (wait_for_input == false) {
+ /* Avoid copy-paste logic. */
+ wmEvent event_modal = {
+ .prevval = KM_NOTHING,
+ .type = EVT_MODAL_MAP,
+ .val = KNF_MODAL_ADD_CUT,
+ };
+ int ret = knifetool_modal(C, op, &event_modal);
+ BLI_assert(ret == OPERATOR_RUNNING_MODAL);
+ UNUSED_VARS_NDEBUG(ret);
+ }
+
+ knife_update_header(C, op, kcd);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
void MESH_OT_knife_tool(wmOperatorType *ot)
{
/* description */
@@ -3035,9 +3133,13 @@ void MESH_OT_knife_tool(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Knife tool as a utility function
- * that can be used for internal slicing operations */
+/** \name Knife tool as a utility function
+ *
+ * Can be used for internal slicing operations.
+ * \{ */
static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
{
@@ -3212,3 +3314,5 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug
kcd = NULL;
}
}
+
+/** \} */
diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c
index 2cb8da37260..b7f671a4157 100644
--- a/source/blender/editors/mesh/editmesh_path.c
+++ b/source/blender/editors/mesh/editmesh_path.c
@@ -669,18 +669,17 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
return edbm_shortest_path_pick_exec(C, op);
}
- Base *basact = NULL;
BMVert *eve = NULL;
BMEdge *eed = NULL;
BMFace *efa = NULL;
ViewContext vc;
- BMEditMesh *em;
bool track_active = true;
em_setup_viewcontext(C, &vc);
copy_v2_v2_int(vc.mval, event->mval);
- em = vc.em;
+ Base *basact = BASACT(vc.view_layer);
+ BMEditMesh *em = vc.em;
view3d_operator_needs_opengl(C);
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 35608a4abde..6cb103460f6 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -268,7 +268,8 @@ static void findnearestvert__doClosest(void *userData,
/**
* Nearest vertex under the cursor.
*
- * \param r_dist: (in/out), minimal distance to the nearest and at the end, actual distance
+ * \param dist_px_manhattan_p: (in/out), minimal distance to the nearest and at the end,
+ * actual distance.
* \param use_select_bias:
* - When true, selected vertices are given a 5 pixel bias
* to make them further than unselect verts.
@@ -276,7 +277,7 @@ static void findnearestvert__doClosest(void *userData,
* \param use_cycle: Cycle over elements within #FIND_NEAR_CYCLE_THRESHOLD_MIN in order of index.
*/
BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan_p,
const bool use_select_bias,
bool use_cycle,
Base **bases,
@@ -286,7 +287,8 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
uint base_index = 0;
if (!XRAY_FLAG_ENABLED(vc->v3d)) {
- uint dist_px = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, *r_dist);
+ uint dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region,
+ *dist_px_manhattan_p);
uint index;
BMVert *eve;
@@ -295,7 +297,7 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_VERTEX);
index = DRW_select_buffer_find_nearest_to_point(
- vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px);
+ vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
if (index) {
eve = (BMVert *)edbm_select_id_bm_elem_get(bases, index, &base_index);
@@ -306,11 +308,11 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
}
if (eve) {
- if (dist_px < *r_dist) {
+ if (dist_px_manhattan_test < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = dist_px;
+ *dist_px_manhattan_p = dist_px_manhattan_test;
return eve;
}
}
@@ -348,18 +350,19 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
data.cycle_index_prev = 0;
}
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
+ *dist_px_manhattan_p;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, clip_flag);
hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
- if (hit->dist < *r_dist) {
+ if (hit->dist < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = hit->dist;
+ *dist_px_manhattan_p = hit->dist;
prev_select_bm = vc->em->bm;
}
}
@@ -375,10 +378,10 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
return hit->vert;
}
-BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist)
+BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
- return EDBM_vert_find_nearest_ex(vc, r_dist, false, false, &base, 1, NULL);
+ return EDBM_vert_find_nearest_ex(vc, dist_px_manhattan_p, false, false, &base, 1, NULL);
}
/* find the distance to the edge we already have */
@@ -417,7 +420,7 @@ struct NearestEdgeUserData_Hit {
/* edges only, un-biased manhattan distance to which ever edge we pick
* (not used for choosing) */
- float dist_center;
+ float dist_center_px_manhattan;
};
struct NearestEdgeUserData {
@@ -477,7 +480,7 @@ static void find_nearest_edge__doClosest(
data->hit.edge = eed;
mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
- data->hit.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
+ data->hit.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
}
if (data->use_cycle) {
@@ -491,14 +494,14 @@ static void find_nearest_edge__doClosest(
data->hit_cycle.edge = eed;
mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
- data->hit_cycle.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
+ data->hit_cycle.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
}
}
}
BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
- float *r_dist,
- float *r_dist_center,
+ float *dist_px_manhattan_p,
+ float *r_dist_center_px_manhattan,
const bool use_select_bias,
bool use_cycle,
BMEdge **r_eed_zbuf,
@@ -509,7 +512,8 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
uint base_index = 0;
if (!XRAY_FLAG_ENABLED(vc->v3d)) {
- uint dist_px = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, *r_dist);
+ uint dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region,
+ *dist_px_manhattan_p);
uint index;
BMEdge *eed;
@@ -518,7 +522,7 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_EDGE);
index = DRW_select_buffer_find_nearest_to_point(
- vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px);
+ vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
if (index) {
eed = (BMEdge *)edbm_select_id_bm_elem_get(bases, index, &base_index);
@@ -533,7 +537,7 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
}
/* exception for faces (verts don't need this) */
- if (r_dist_center && eed) {
+ if (r_dist_center_px_manhattan && eed) {
struct NearestEdgeUserData_ZBuf data;
data.mval_fl[0] = vc->mval[0];
@@ -546,16 +550,16 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
mesh_foreachScreenEdge(
vc, find_nearest_edge_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
- *r_dist_center = data.dist;
+ *r_dist_center_px_manhattan = data.dist;
}
/* end exception */
if (eed) {
- if (dist_px < *r_dist) {
+ if (dist_px_manhattan_test < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = dist_px;
+ *dist_px_manhattan_p = dist_px_manhattan_test;
return eed;
}
}
@@ -593,18 +597,19 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
data.cycle_index_prev = 0;
}
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
+ *dist_px_manhattan_p;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
mesh_foreachScreenEdge(vc, find_nearest_edge__doClosest, &data, clip_flag);
hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
- if (hit->dist < *r_dist) {
+ if (hit->dist < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = hit->dist;
+ *dist_px_manhattan_p = hit->dist;
prev_select_bm = vc->em->bm;
}
}
@@ -613,8 +618,8 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
return NULL;
}
- if (r_dist_center) {
- *r_dist_center = hit->dist_center;
+ if (r_dist_center_px_manhattan) {
+ *r_dist_center_px_manhattan = hit->dist_center_px_manhattan;
}
prev_select.index = hit->index;
@@ -624,16 +629,17 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
return hit->edge;
}
-BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *r_dist)
+BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
- return EDBM_edge_find_nearest_ex(vc, r_dist, NULL, false, false, NULL, &base, 1, NULL);
+ return EDBM_edge_find_nearest_ex(
+ vc, dist_px_manhattan_p, NULL, false, false, NULL, &base, 1, NULL);
}
/* find the distance to the face we already have */
struct NearestFaceUserData_ZBuf {
float mval_fl[2];
- float dist;
+ float dist_px_manhattan;
const BMFace *face_test;
};
@@ -647,8 +653,8 @@ static void find_nearest_face_center__doZBuf(void *userData,
if (efa == data->face_test) {
const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
- if (dist_test < data->dist) {
- data->dist = dist_test;
+ if (dist_test < data->dist_px_manhattan) {
+ data->dist_px_manhattan = dist_test;
}
}
}
@@ -702,9 +708,17 @@ static void findnearestface__doClosest(void *userData,
}
}
+/**
+ * \param use_zbuf_single_px: Special case, when using the back-buffer selection,
+ * only use the pixel at `vc->mval` instead of using `dist_px_manhattan_p` to search over a larger
+ * region. This is needed because historically selection worked this way for a long time, however
+ * it's reasonable that some callers might want to expand the region too. So add an argument to do
+ * this,
+ */
BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan_p,
float *r_dist_center,
+ const bool use_zbuf_single_px,
const bool use_select_bias,
bool use_cycle,
BMFace **r_efa_zbuf,
@@ -715,14 +729,28 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
uint base_index = 0;
if (!XRAY_FLAG_ENABLED(vc->v3d)) {
- float dist_test = 0.0f;
+ float dist_test;
uint index;
BMFace *efa;
{
+ uint dist_px_manhattan_test = 0;
+ if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) {
+ dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region,
+ *dist_px_manhattan_p);
+ }
+
DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_FACE);
- index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval);
+ if (dist_px_manhattan_test == 0) {
+ index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval);
+ dist_test = 0.0f;
+ }
+ else {
+ index = DRW_select_buffer_find_nearest_to_point(
+ vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
+ dist_test = dist_px_manhattan_test;
+ }
if (index) {
efa = (BMFace *)edbm_select_id_bm_elem_get(bases, index, &base_index);
@@ -742,7 +770,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
data.mval_fl[0] = vc->mval[0];
data.mval_fl[1] = vc->mval[1];
- data.dist = FLT_MAX;
+ data.dist_px_manhattan = FLT_MAX;
data.face_test = efa;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
@@ -750,16 +778,16 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
mesh_foreachScreenFace(
vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
- *r_dist_center = data.dist;
+ *r_dist_center = data.dist_px_manhattan;
}
/* end exception */
if (efa) {
- if (dist_test < *r_dist) {
+ if (dist_test < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = dist_test;
+ *dist_px_manhattan_p = dist_test;
return efa;
}
}
@@ -795,18 +823,19 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
data.cycle_index_prev = 0;
}
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
+ *dist_px_manhattan_p;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag);
hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
- if (hit->dist < *r_dist) {
+ if (hit->dist < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = hit->dist;
+ *dist_px_manhattan_p = hit->dist;
prev_select_bm = vc->em->bm;
}
}
@@ -826,10 +855,11 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
return hit->face;
}
-BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist)
+BMFace *EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
- return EDBM_face_find_nearest_ex(vc, r_dist, NULL, false, false, NULL, &base, 1, NULL);
+ return EDBM_face_find_nearest_ex(
+ vc, dist_px_manhattan_p, NULL, false, false, false, NULL, &base, 1, NULL);
}
#undef FIND_NEAR_SELECT_BIAS
@@ -883,7 +913,7 @@ static bool unified_findnearest(ViewContext *vc,
uint base_index = 0;
BMFace *efa_zbuf = NULL;
BMFace *efa_test = EDBM_face_find_nearest_ex(
- vc, &dist, dist_center_p, true, use_cycle, &efa_zbuf, bases, bases_len, &base_index);
+ vc, &dist, dist_center_p, true, true, use_cycle, &efa_zbuf, bases, bases_len, &base_index);
if (efa_test && dist_center_p) {
dist = min_ff(dist_margin, dist_center);
@@ -2517,6 +2547,7 @@ bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
changed = true;
}
}
+ MEM_freeN(objects);
if (changed) {
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
@@ -4602,35 +4633,56 @@ static int edbm_select_random_exec(bContext *C, wmOperator *op)
seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
}
- RNG *rng = BLI_rng_new_srandom(seed_iter);
-
if (em->selectmode & SCE_SELECT_VERTEX) {
+ int elem_map_len = 0;
+ BMVert **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totvert, __func__);
BMVert *eve;
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
- if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN) && BLI_rng_get_float(rng) < randfac) {
- BM_vert_select_set(em->bm, eve, select);
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ elem_map[elem_map_len++] = eve;
}
}
+
+ BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed);
+ const int count_select = elem_map_len * randfac;
+ for (int i = 0; i < count_select; i++) {
+ BM_vert_select_set(em->bm, elem_map[i], select);
+ }
+ MEM_freeN(elem_map);
}
else if (em->selectmode & SCE_SELECT_EDGE) {
+ int elem_map_len = 0;
+ BMEdge **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totedge, __func__);
BMEdge *eed;
BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
- if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && BLI_rng_get_float(rng) < randfac) {
- BM_edge_select_set(em->bm, eed, select);
+ if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+ elem_map[elem_map_len++] = eed;
}
}
+ BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed);
+ const int count_select = elem_map_len * randfac;
+ for (int i = 0; i < count_select; i++) {
+ BM_edge_select_set(em->bm, elem_map[i], select);
+ }
+ MEM_freeN(elem_map);
}
else {
+ int elem_map_len = 0;
+ BMFace **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totface, __func__);
BMFace *efa;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && BLI_rng_get_float(rng) < randfac) {
- BM_face_select_set(em->bm, efa, select);
+ if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ elem_map[elem_map_len++] = efa;
}
}
+ BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed);
+ const int count_select = elem_map_len * randfac;
+ for (int i = 0; i < count_select; i++) {
+ BM_face_select_set(em->bm, elem_map[i], select);
+ }
+ MEM_freeN(elem_map);
}
- BLI_rng_free(rng);
-
if (select) {
/* was EDBM_select_flush, but it over select in edge/face mode */
EDBM_selectmode_flush(em);
@@ -4795,15 +4847,8 @@ static int edbm_select_axis_exec(bContext *C, wmOperator *op)
float axis_mat[3][3];
/* 3D view variables may be NULL, (no need to check in poll function). */
- ED_transform_calc_orientation_from_type_ex(C,
- axis_mat,
- scene,
- CTX_wm_region_view3d(C),
- obedit,
- obedit,
- orientation,
- 0,
- V3D_AROUND_ACTIVE);
+ ED_transform_calc_orientation_from_type_ex(
+ C, axis_mat, scene, CTX_wm_region_view3d(C), obedit, obedit, orientation, V3D_AROUND_ACTIVE);
const float *axis_vector = axis_mat[axis];
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index f9651454dee..f3c0da67ecc 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -1325,7 +1325,9 @@ void MESH_OT_select_similar(wmOperatorType *ot)
RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
- RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
+ prop = RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
+ /* Very small values are needed sometimes, similar area of small faces for e.g: see T87823 */
+ RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 5);
}
/** \} */
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index ade21f58232..bb332a4094c 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -3694,20 +3694,18 @@ static const EnumPropertyItem *shape_itemf(bContext *C,
static void edbm_blend_from_shape_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
- PointerRNA ptr;
Object *obedit = CTX_data_edit_object(C);
Mesh *me = obedit->data;
PointerRNA ptr_key;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
RNA_id_pointer_create((ID *)me->key, &ptr_key);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- uiItemPointerR(layout, &ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA);
- uiItemR(layout, &ptr, "blend", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "add", 0, NULL, ICON_NONE);
+ uiItemPointerR(layout, op->ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA);
+ uiItemR(layout, op->ptr, "blend", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "add", 0, NULL, ICON_NONE);
}
void MESH_OT_blend_from_shape(wmOperatorType *ot)
@@ -5614,25 +5612,22 @@ static bool edbm_decimate_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
static void edbm_decimate_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout, *row, *col, *sub;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "ratio", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "ratio", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_vertex_group", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_vertex_group", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_vertex_group"));
- uiItemR(col, &ptr, "vertex_group_factor", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "invert_vertex_group", 0, NULL, ICON_NONE);
+ uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "use_vertex_group"));
+ uiItemR(col, op->ptr, "vertex_group_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "invert_vertex_group", 0, NULL, ICON_NONE);
row = uiLayoutRowWithHeading(layout, true, IFACE_("Symmetry"));
- uiItemR(row, &ptr, "use_symmetry", 0, "", ICON_NONE);
+ uiItemR(row, op->ptr, "use_symmetry", 0, "", ICON_NONE);
sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_symmetry"));
- uiItemR(sub, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "use_symmetry"));
+ uiItemR(sub, op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
}
void MESH_OT_decimate(wmOperatorType *ot)
@@ -6886,8 +6881,8 @@ void MESH_OT_sort_elements(wmOperatorType *ot)
"SELECTED",
0,
"Selected",
- "Move all selected elements in first places, preserving their relative order "
- "(WARNING: this will affect unselected elements' indices as well!)"},
+ "Move all selected elements in first places, preserving their relative order.\n"
+ "Warning: This will affect unselected elements' indices as well"},
{SRT_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Randomize order of selected elements"},
{SRT_REVERSE, "REVERSE", 0, "Reverse", "Reverse current order of selected elements"},
{0, NULL, 0, NULL, NULL},
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 79385e28aa9..274f4cdbb6c 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -93,6 +93,12 @@ typedef struct BArrayCustomData {
#endif
typedef struct UndoMesh {
+ /**
+ * This undo-meshes in `um_arraystore.local_links`.
+ * Not to be confused with the next and previous undo steps.
+ */
+ struct UndoMesh *local_next, *local_prev;
+
Mesh me;
int selectmode;
@@ -128,7 +134,10 @@ static struct {
struct BArrayStore_AtSize bs_stride;
int users;
- /* We could have the undo API pass in the previous state, for now store a local list */
+ /**
+ * A list of #UndoMesh items ordered from oldest to newest
+ * used to access previous undo data for a mesh.
+ */
ListBase local_links;
# ifdef USE_ARRAY_STORE_THREAD
@@ -520,11 +529,63 @@ static void um_arraystore_free(UndoMesh *um)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Array Store Utilities
+ * \{ */
+
+/**
+ * Create an array of #UndoMesh from `objects`.
+ *
+ * where each element in the resulting array is the most recently created
+ * undo-mesh for the object's mesh.
+ * When no undo-mesh can be found that array index is NULL.
+ *
+ * This is used for de-duplicating memory between undo steps,
+ * failure to find the undo step will store a full duplicate in memory.
+ * define `DEBUG_PRINT` to check memory is de-duplicating as expected.
+ */
+static UndoMesh **mesh_undostep_reference_elems_from_objects(Object **object, int object_len)
+{
+ /* Map: `Mesh.id.session_uuid` -> `UndoMesh`. */
+ GHash *uuid_map = BLI_ghash_ptr_new_ex(__func__, object_len);
+ UndoMesh **um_references = MEM_callocN(sizeof(UndoMesh *) * object_len, __func__);
+ for (int i = 0; i < object_len; i++) {
+ const Mesh *me = object[i]->data;
+ BLI_ghash_insert(uuid_map, POINTER_FROM_INT(me->id.session_uuid), &um_references[i]);
+ }
+ int uuid_map_len = object_len;
+
+ /* Loop backwards over all previous mesh undo data until either:
+ * - All elements have been found (where `um_references` we'll have every element set).
+ * - There are no undo steps left to look for. */
+ UndoMesh *um_iter = um_arraystore.local_links.last;
+ while (um_iter && (uuid_map_len != 0)) {
+ UndoMesh **um_p;
+ if ((um_p = BLI_ghash_popkey(uuid_map, POINTER_FROM_INT(um_iter->me.id.session_uuid), NULL))) {
+ *um_p = um_iter;
+ uuid_map_len--;
+ }
+ um_iter = um_iter->local_prev;
+ }
+ BLI_assert(uuid_map_len == BLI_ghash_len(uuid_map));
+ BLI_ghash_free(uuid_map, NULL, NULL);
+ if (uuid_map_len == object_len) {
+ MEM_freeN(um_references);
+ um_references = NULL;
+ }
+ return um_references;
+}
+
+/** \} */
+
#endif /* USE_ARRAY_STORE */
/* for callbacks */
/* undo simply makes copies of a bmesh */
-static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
+/**
+ * \param um_ref: The reference to use for de-duplicating memory between undo-steps.
+ */
+static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, UndoMesh *um_ref)
{
BLI_assert(BLI_array_is_zeroed(um, 1));
#ifdef USE_ARRAY_STORE_THREAD
@@ -560,18 +621,12 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
#ifdef USE_ARRAY_STORE
{
- /* We could be more clever here,
- * the previous undo state may be from a separate mesh. */
- const UndoMesh *um_ref = um_arraystore.local_links.last ?
- ((LinkData *)um_arraystore.local_links.last)->data :
- NULL;
-
/* Add ourselves. */
- BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um));
+ BLI_addtail(&um_arraystore.local_links, um);
# ifdef USE_ARRAY_STORE_THREAD
if (um_arraystore.task_pool == NULL) {
- um_arraystore.task_pool = BLI_task_pool_create_background(NULL, TASK_PRIORITY_LOW);
+ um_arraystore.task_pool = BLI_task_pool_create_background(NULL, TASK_PRIORITY_LOW, true);
}
struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__);
@@ -583,6 +638,8 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
um_arraystore_compact_with_info(um, um_ref);
# endif
}
+#else
+ UNUSED_VARS(um_ref);
#endif
return um;
@@ -682,11 +739,9 @@ static void undomesh_free_data(UndoMesh *um)
/* we need to expand so any allocations in custom-data are freed with the mesh */
um_arraystore_expand(um);
- {
- LinkData *link = BLI_findptr(&um_arraystore.local_links, um, offsetof(LinkData, data));
- BLI_remlink(&um_arraystore.local_links, link);
- MEM_freeN(link);
- }
+ BLI_assert(BLI_findindex(&um_arraystore.local_links, um) != -1);
+ BLI_remlink(&um_arraystore.local_links, um);
+
um_arraystore_free(um);
#endif
@@ -720,7 +775,6 @@ static Object *editmesh_object_from_context(bContext *C)
* \{ */
typedef struct MeshUndoStep_Elem {
- struct MeshUndoStep_Elem *next, *prev;
UndoRefID_Object obedit_ref;
UndoMesh data;
} MeshUndoStep_Elem;
@@ -749,6 +803,12 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und
us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
us->elems_len = objects_len;
+ UndoMesh **um_references = NULL;
+
+#ifdef USE_ARRAY_STORE
+ um_references = mesh_undostep_reference_elems_from_objects(objects, objects_len);
+#endif
+
for (uint i = 0; i < objects_len; i++) {
Object *ob = objects[i];
MeshUndoStep_Elem *elem = &us->elems[i];
@@ -756,12 +816,22 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und
elem->obedit_ref.ptr = ob;
Mesh *me = elem->obedit_ref.ptr->data;
BMEditMesh *em = me->edit_mesh;
- undomesh_from_editmesh(&elem->data, me->edit_mesh, me->key);
+ undomesh_from_editmesh(
+ &elem->data, me->edit_mesh, me->key, um_references ? um_references[i] : NULL);
em->needs_flush_to_id = 1;
us->step.data_size += elem->data.undo_size;
+
+#ifdef USE_ARRAY_STORE
+ /** As this is only data storage it is safe to set the session ID here. */
+ elem->data.me.id.session_uuid = me->id.session_uuid;
+#endif
}
MEM_freeN(objects);
+ if (um_references != NULL) {
+ MEM_freeN(um_references);
+ }
+
bmain->is_memfile_undo_flush_needed = true;
return true;
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 94f386e08d5..19c9909039c 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -785,7 +785,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
l = v->l;
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv = luv->uv;
- uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
+ uv_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
lastv = NULL;
iterv = vlist;
@@ -796,7 +796,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
l = iterv->l;
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv2 = luv->uv;
- uv2_vert_sel = luv->flag & MLOOPUV_VERTSEL;
+ uv2_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
/* Check if the uv loops share the same selection state (if not, they are not connected as
* they have been ripped or other edit commands have separated them). */
@@ -1721,7 +1721,7 @@ void EDBM_project_snap_verts(
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_NOT_ACTIVE,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
mval,
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index 3450d61337c..f306612f295 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -149,7 +149,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
mul_m4_m4m4(cmat, imat, ob_src->obmat);
/* transform vertex coordinates into new space */
- for (a = 0, mvert = *mvert_pp; a < me->totvert; a++, mvert++) {
+ for (a = 0; a < me->totvert; a++, mvert++) {
mul_m4_v3(cmat, mvert->co);
}
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 77b5379ddd4..4338b043fc9 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -87,7 +87,7 @@ if(WITH_INTERNATIONAL)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
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 50dc1af5ca8..aefcf68390e 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -117,6 +117,7 @@
#include "ED_physics.h"
#include "ED_render.h"
#include "ED_screen.h"
+#include "ED_select_utils.h"
#include "ED_transform.h"
#include "ED_view3d.h"
@@ -455,51 +456,53 @@ void ED_object_add_mesh_props(wmOperatorType *ot)
bool ED_object_add_generic_get_opts(bContext *C,
wmOperator *op,
const char view_align_axis,
- float loc[3],
- float rot[3],
- float scale[3],
- bool *enter_editmode,
- ushort *local_view_bits,
- bool *is_view_aligned)
-{
- PropertyRNA *prop;
-
- /* Switch to Edit mode? optional prop */
- if ((prop = RNA_struct_find_property(op->ptr, "enter_editmode"))) {
+ float r_loc[3],
+ float r_rot[3],
+ float r_scale[3],
+ bool *r_enter_editmode,
+ ushort *r_local_view_bits,
+ bool *r_is_view_aligned)
+{
+ /* Edit Mode! (optional) */
+ {
bool _enter_editmode;
- if (!enter_editmode) {
- enter_editmode = &_enter_editmode;
+ if (!r_enter_editmode) {
+ r_enter_editmode = &_enter_editmode;
}
+ /* Only to ensure the value is _always_ set.
+ * Typically the property will exist when the argument is non-NULL. */
+ *r_enter_editmode = false;
- if (RNA_property_is_set(op->ptr, prop) && enter_editmode) {
- *enter_editmode = RNA_property_boolean_get(op->ptr, prop);
- }
- else {
- *enter_editmode = (U.flag & USER_ADD_EDITMODE) != 0;
- RNA_property_boolean_set(op->ptr, prop, *enter_editmode);
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "enter_editmode");
+ if (prop != NULL) {
+ if (RNA_property_is_set(op->ptr, prop) && r_enter_editmode) {
+ *r_enter_editmode = RNA_property_boolean_get(op->ptr, prop);
+ }
+ else {
+ *r_enter_editmode = (U.flag & USER_ADD_EDITMODE) != 0;
+ RNA_property_boolean_set(op->ptr, prop, *r_enter_editmode);
+ }
}
}
- if (local_view_bits) {
+ if (r_local_view_bits) {
View3D *v3d = CTX_wm_view3d(C);
- if (v3d && v3d->localvd) {
- *local_view_bits = v3d->local_view_uuid;
- }
+ *r_local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
}
/* Location! */
{
float _loc[3];
- if (!loc) {
- loc = _loc;
+ if (!r_loc) {
+ r_loc = _loc;
}
if (RNA_struct_property_is_set(op->ptr, "location")) {
- RNA_float_get_array(op->ptr, "location", loc);
+ RNA_float_get_array(op->ptr, "location", r_loc);
}
else {
- ED_object_location_from_view(C, loc);
- RNA_float_set_array(op->ptr, "location", loc);
+ ED_object_location_from_view(C, r_loc);
+ RNA_float_set_array(op->ptr, "location", r_loc);
}
}
@@ -507,33 +510,33 @@ bool ED_object_add_generic_get_opts(bContext *C,
{
bool _is_view_aligned;
float _rot[3];
- if (!is_view_aligned) {
- is_view_aligned = &_is_view_aligned;
+ if (!r_is_view_aligned) {
+ r_is_view_aligned = &_is_view_aligned;
}
- if (!rot) {
- rot = _rot;
+ if (!r_rot) {
+ r_rot = _rot;
}
if (RNA_struct_property_is_set(op->ptr, "rotation")) {
/* If rotation is set, always use it. Alignment (and corresponding user preference)
* can be ignored since this is in world space anyways.
* To not confuse (e.g. on redo), don't set it to #ALIGN_WORLD in the op UI though. */
- *is_view_aligned = false;
- RNA_float_get_array(op->ptr, "rotation", rot);
+ *r_is_view_aligned = false;
+ RNA_float_get_array(op->ptr, "rotation", r_rot);
}
else {
int alignment = ALIGN_WORLD;
- prop = RNA_struct_find_property(op->ptr, "align");
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "align");
if (RNA_property_is_set(op->ptr, prop)) {
/* If alignment is set, always use it. */
- *is_view_aligned = alignment == ALIGN_VIEW;
+ *r_is_view_aligned = alignment == ALIGN_VIEW;
alignment = RNA_property_enum_get(op->ptr, prop);
}
else {
/* If alignment is not set, use User Preferences. */
- *is_view_aligned = (U.flag & USER_ADD_VIEWALIGNED) != 0;
- if (*is_view_aligned) {
+ *r_is_view_aligned = (U.flag & USER_ADD_VIEWALIGNED) != 0;
+ if (*r_is_view_aligned) {
RNA_property_enum_set(op->ptr, prop, ALIGN_VIEW);
alignment = ALIGN_VIEW;
}
@@ -548,18 +551,18 @@ bool ED_object_add_generic_get_opts(bContext *C,
}
switch (alignment) {
case ALIGN_WORLD:
- RNA_float_get_array(op->ptr, "rotation", rot);
+ RNA_float_get_array(op->ptr, "rotation", r_rot);
break;
case ALIGN_VIEW:
- ED_object_rotation_from_view(C, rot, view_align_axis);
- RNA_float_set_array(op->ptr, "rotation", rot);
+ ED_object_rotation_from_view(C, r_rot, view_align_axis);
+ RNA_float_set_array(op->ptr, "rotation", r_rot);
break;
case ALIGN_CURSOR: {
const Scene *scene = CTX_data_scene(C);
float tmat[3][3];
BKE_scene_cursor_rot_to_mat3(&scene->cursor, tmat);
- mat3_normalized_to_eul(rot, tmat);
- RNA_float_set_array(op->ptr, "rotation", rot);
+ mat3_normalized_to_eul(r_rot, tmat);
+ RNA_float_set_array(op->ptr, "rotation", r_rot);
break;
}
}
@@ -569,19 +572,21 @@ bool ED_object_add_generic_get_opts(bContext *C,
/* Scale! */
{
float _scale[3];
- if (!scale) {
- scale = _scale;
+ if (!r_scale) {
+ r_scale = _scale;
}
/* For now this is optional, we can make it always use. */
- copy_v3_fl(scale, 1.0f);
- if ((prop = RNA_struct_find_property(op->ptr, "scale"))) {
+ copy_v3_fl(r_scale, 1.0f);
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "scale");
+ if (prop != NULL) {
if (RNA_property_is_set(op->ptr, prop)) {
- RNA_property_float_get_array(op->ptr, prop, scale);
+ RNA_property_float_get_array(op->ptr, prop, r_scale);
}
else {
- copy_v3_fl(scale, 1.0f);
- RNA_property_float_set_array(op->ptr, prop, scale);
+ copy_v3_fl(r_scale, 1.0f);
+ RNA_property_float_set_array(op->ptr, prop, r_scale);
}
}
}
@@ -861,8 +866,9 @@ static int effector_add_exec(bContext *C, wmOperator *op)
float mat[4][4];
ED_object_new_primitive_matrix(C, ob, loc, rot, mat);
+ mul_mat3_m4_fl(mat, dia);
BLI_addtail(&cu->editnurb->nurbs,
- ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, dia));
+ ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1));
if (!enter_editmode) {
ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
}
@@ -1311,6 +1317,9 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL;
const int type = RNA_enum_get(op->ptr, "type");
+ const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front");
+ const bool use_lights = RNA_boolean_get(op->ptr, "use_lights");
+ const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order");
ushort local_view_bits;
float loc[3], rot[3];
@@ -1321,12 +1330,14 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
return OPERATOR_CANCELLED;
}
- /* add new object if not currently editing a GP object,
- * or if "empty" was chosen (i.e. user wants a blank GP canvas)
- */
- if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false) || (type == GP_EMPTY)) {
+ /* Add new object if not currently editing a GP object. */
+ if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) {
const char *ob_name = NULL;
switch (type) {
+ case GP_EMPTY: {
+ ob_name = "GPencil";
+ break;
+ }
case GP_MONKEY: {
ob_name = "Suzanne";
break;
@@ -1336,6 +1347,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
break;
}
case GP_LRT_OBJECT:
+ case GP_LRT_SCENE:
case GP_LRT_COLLECTION: {
ob_name = "Line Art";
break;
@@ -1356,6 +1368,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
/* create relevant geometry */
switch (type) {
+ case GP_EMPTY: {
+ float mat[4][4];
+
+ ED_object_new_primitive_matrix(C, ob, loc, rot, mat);
+ ED_gpencil_create_blank(C, ob, mat);
+ break;
+ }
case GP_STROKE: {
float radius = RNA_float_get(op->ptr, "radius");
float mat[4][4];
@@ -1416,14 +1435,29 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
/* Only created one layer and one material. */
strcpy(md->target_layer, ((bGPDlayer *)gpd->layers.first)->info);
md->target_material = BKE_gpencil_material(ob, 1);
+ if (md->target_material) {
+ id_us_plus(&md->target_material->id);
+ }
+
+ if (use_lights) {
+ ob->dtx |= OB_USE_GPENCIL_LIGHTS;
+ }
+ else {
+ ob->dtx &= ~OB_USE_GPENCIL_LIGHTS;
+ }
/* Stroke object is drawn in front of meshes by default. */
- ob->dtx |= OB_DRAW_IN_FRONT;
- }
- case GP_EMPTY:
- /* do nothing */
- break;
+ if (use_in_front) {
+ ob->dtx |= OB_DRAW_IN_FRONT;
+ }
+ else {
+ if (stroke_depth_order == GP_DRAWMODE_3D) {
+ gpd->draw_mode = GP_DRAWMODE_3D;
+ }
+ }
+ break;
+ }
default:
BKE_report(op->reports, RPT_WARNING, "Not implemented");
break;
@@ -1440,6 +1474,39 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void object_add_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, op->ptr, "radius", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "align", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "location", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "rotation", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "type", 0, NULL, ICON_NONE);
+
+ int type = RNA_enum_get(op->ptr, "type");
+ if (type == GP_LRT_COLLECTION || type == GP_LRT_OBJECT || type == GP_LRT_SCENE) {
+ uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE);
+ bool in_front = RNA_boolean_get(op->ptr, "use_in_front");
+ uiLayout *row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, !in_front);
+ uiItemR(row, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE);
+ }
+}
+
+static EnumPropertyItem rna_enum_gpencil_add_stroke_depth_order_items[] = {
+ {GP_DRAWMODE_2D,
+ "2D",
+ 0,
+ "2D Layers",
+ "Display strokes using grease pencil layers to define order"},
+ {GP_DRAWMODE_3D, "3D", 0, "3D Location", "Display strokes using real 3D position in 3D space"},
+ {0, NULL, 0, NULL, NULL},
+};
+
void OBJECT_OT_gpencil_add(wmOperatorType *ot)
{
/* identifiers */
@@ -1455,11 +1522,28 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* ui */
+ ot->ui = object_add_ui;
+
/* properties */
ED_object_add_unit_props_radius(ot);
ED_object_add_generic_props(ot, false);
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", "");
+ RNA_def_boolean(ot->srna,
+ "use_in_front",
+ false,
+ "In Front",
+ "Show line art grease pencil in front of everything");
+ RNA_def_boolean(
+ ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object");
+ RNA_def_enum(
+ ot->srna,
+ "stroke_depth_order",
+ rna_enum_gpencil_add_stroke_depth_order_items,
+ GP_DRAWMODE_3D,
+ "Stroke Depth Order",
+ "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front'");
}
/** \} */
@@ -1896,8 +1980,8 @@ void OBJECT_OT_pointcloud_add(wmOperatorType *ot)
/* note: now unlinks constraints as well */
void ED_object_base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
{
- if (BKE_library_ID_is_indirectly_used(bmain, ob) && ID_REAL_USERS(ob) <= 1 &&
- ID_EXTRA_USERS(ob) == 0) {
+ if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, ob)) {
/* We cannot delete indirectly used object... */
printf(
"WARNING, undeletable object '%s', should have been caught before reaching this "
@@ -1911,6 +1995,17 @@ void ED_object_base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
BKE_scene_collections_object_remove(bmain, scene, ob, true);
}
+/**
+ * Remove base from a specific scene.
+ * `ob` must not be indirectly used.
+ */
+void ED_object_base_free_and_unlink_no_indirect_check(Main *bmain, Scene *scene, Object *ob)
+{
+ BLI_assert(!BKE_library_ID_is_indirectly_used(bmain, ob));
+ DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_BASE_FLAGS);
+ BKE_scene_collections_object_remove(bmain, scene, ob, true);
+}
+
static int object_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1918,13 +2013,15 @@ static int object_delete_exec(bContext *C, wmOperator *op)
wmWindowManager *wm = CTX_wm_manager(C);
const bool use_global = RNA_boolean_get(op->ptr, "use_global");
uint changed_count = 0;
+ uint tagged_count = 0;
if (CTX_data_edit_object(C)) {
return OPERATOR_CANCELLED;
}
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
- const bool is_indirectly_used = BKE_library_ID_is_indirectly_used(bmain, ob);
if (ob->id.tag & LIB_TAG_INDIRECT) {
/* Can this case ever happen? */
BKE_reportf(op->reports,
@@ -1933,7 +2030,9 @@ static int object_delete_exec(bContext *C, wmOperator *op)
ob->id.name + 2);
continue;
}
- if (is_indirectly_used && ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0) {
+
+ if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, ob)) {
BKE_reportf(op->reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at "
@@ -1949,63 +2048,41 @@ static int object_delete_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
- /* This is sort of a quick hack to address T51243 -
- * Proper thing to do here would be to nuke most of all this custom scene/object/base handling,
- * and use generic lib remap/query for that.
- * But this is for later (aka 2.8, once layers & co are settled and working).
- */
- if (use_global && ob->id.lib == NULL) {
- /* We want to nuke the object, let's nuke it the easy way (not for linked data though)... */
- BKE_id_delete(bmain, &ob->id);
- changed_count += 1;
- continue;
+ /* Use multi tagged delete if `use_global=True`, or the object is used only in one scene. */
+ if (use_global || ID_REAL_USERS(ob) <= 1) {
+ ob->id.tag |= LIB_TAG_DOIT;
+ tagged_count += 1;
}
+ else {
+ /* Object is used in multiple scenes. Delete the object from the current scene only. */
+ ED_object_base_free_and_unlink_no_indirect_check(bmain, scene, ob);
+ changed_count += 1;
- /* remove from Grease Pencil parent */
- /* XXX This is likely not correct?
- * Will also remove parent from grease pencil from other scenes,
- * even when use_global is false... */
- for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) {
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- if (gpl->parent != NULL) {
- if (gpl->parent == ob) {
- gpl->parent = NULL;
- }
- }
- }
- }
-
- /* remove from current scene only */
- ED_object_base_free_and_unlink(bmain, scene, ob);
- changed_count += 1;
-
- if (use_global) {
- Scene *scene_iter;
- for (scene_iter = bmain->scenes.first; scene_iter; scene_iter = scene_iter->id.next) {
- if (scene_iter != scene && !ID_IS_LINKED(scene_iter)) {
- if (is_indirectly_used && ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0) {
- BKE_reportf(op->reports,
- RPT_WARNING,
- "Cannot delete object '%s' from scene '%s', indirectly used objects need "
- "at least one user",
- ob->id.name + 2,
- scene_iter->id.name + 2);
- break;
+ /* FIXME: this will also remove parent from grease pencil from other scenes. */
+ /* Remove from Grease Pencil parent */
+ for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (gpl->parent != NULL) {
+ if (gpl->parent == ob) {
+ gpl->parent = NULL;
+ }
}
- ED_object_base_free_and_unlink(bmain, scene_iter, ob);
}
}
}
- /* end global */
}
CTX_DATA_END;
- BKE_reportf(op->reports, RPT_INFO, "Deleted %u object(s)", changed_count);
-
- if (changed_count == 0) {
+ if ((changed_count + tagged_count) == 0) {
return OPERATOR_CANCELLED;
}
+ if (tagged_count > 0) {
+ BKE_id_multi_tagged_delete(bmain);
+ }
+
+ BKE_reportf(op->reports, RPT_INFO, "Deleted %u object(s)", (changed_count + tagged_count));
+
/* delete has to handle all open scenes */
BKE_main_id_tag_listbase(&bmain->scenes, LIB_TAG_DOIT, true);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
@@ -2077,8 +2154,7 @@ static void copy_object_set_idnew(bContext *C)
FOREACH_MAIN_ID_END;
#endif
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
}
/** \} */
@@ -2399,7 +2475,7 @@ static void make_object_duplilist_real(bContext *C,
free_object_duplilist(lb_duplis);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
base->object->transflag &= ~OB_DUPLI;
DEG_id_tag_update(&base->object->id, ID_RECALC_COPY_ON_WRITE);
@@ -2414,7 +2490,7 @@ static int object_duplicates_make_real_exec(bContext *C, wmOperator *op)
const bool use_base_parent = RNA_boolean_get(op->ptr, "use_base_parent");
const bool use_hierarchy = RNA_boolean_get(op->ptr, "use_hierarchy");
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
make_object_duplilist_real(C, depsgraph, scene, base, use_base_parent, use_hierarchy);
@@ -2626,6 +2702,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
int a, mballConverted = 0;
bool gpencilConverted = false;
+ bool gpencilCurveConverted = false;
/* don't forget multiple users! */
@@ -2838,6 +2915,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
me_eval = BKE_mesh_copy_for_eval(me_eval, false);
/* Full (edge-angle based) draw calculation should ideally be performed. */
BKE_mesh_edges_set_draw_render(me_eval);
+ BKE_object_material_from_eval_data(bmain, newob, &me_eval->id);
BKE_mesh_nomain_to_mesh(me_eval, newob->data, newob, &CD_MASK_MESH, true);
BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */
}
@@ -2901,7 +2979,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
}
cu->flag &= ~CU_3D;
- BKE_curve_curve_dimension_update(cu);
+ BKE_curve_dimension_update(cu);
if (target == OB_MESH) {
/* No assumption should be made that the resulting objects is a mesh, as conversion can
@@ -2910,6 +2988,16 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* meshes doesn't use displist */
BKE_object_free_curve_cache(newob);
}
+ else if (target == OB_GPENCIL) {
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ Object *ob_gpencil = ED_gpencil_add_object(C, newob->loc, local_view_bits);
+ copy_v3_v3(ob_gpencil->rot, newob->rot);
+ copy_v3_v3(ob_gpencil->scale, newob->scale);
+ BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f);
+ gpencilConverted = true;
+ gpencilCurveConverted = true;
+ basen = NULL;
+ }
}
else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
ob->flag |= OB_DONE;
@@ -3091,6 +3179,17 @@ static int object_convert_exec(bContext *C, wmOperator *op)
FOREACH_SCENE_OBJECT_END;
}
}
+ else {
+ /* Remove Text curves converted to Grease Pencil object to avoid duplicated curves. */
+ if (gpencilCurveConverted) {
+ FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) {
+ if (ELEM(ob_delete->type, OB_CURVE) && (ob_delete->flag & OB_DONE)) {
+ ED_object_base_free_and_unlink(bmain, scene, ob_delete);
+ }
+ }
+ FOREACH_SCENE_OBJECT_END;
+ }
+ }
// XXX ED_object_editmode_enter(C, 0);
// XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */
@@ -3117,20 +3216,18 @@ static int object_convert_exec(bContext *C, wmOperator *op)
static void object_convert_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- PointerRNA ptr;
uiLayoutSetPropSep(layout, true);
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- uiItemR(layout, &ptr, "target", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "keep_original", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "target", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "keep_original", 0, NULL, ICON_NONE);
- if (RNA_enum_get(&ptr, "target") == OB_GPENCIL) {
- uiItemR(layout, &ptr, "thickness", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "angle", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "seams", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "faces", 0, NULL, ICON_NONE);
+ if (RNA_enum_get(op->ptr, "target") == OB_GPENCIL) {
+ uiItemR(layout, op->ptr, "thickness", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "angle", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "seams", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "faces", 0, NULL, ICON_NONE);
}
}
@@ -3278,7 +3375,7 @@ Base *ED_object_add_duplicate(
DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS);
}
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
return basen;
}
@@ -3294,8 +3391,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
/* We need to handle that here ourselves, because we may duplicate several objects, in which case
* we also want to remap pointers between those... */
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
Base *basen = object_add_duplicate_internal(
@@ -3372,6 +3468,19 @@ void OBJECT_OT_duplicate(wmOperatorType *ot)
* Use for drag & drop.
* \{ */
+static Base *object_add_ensure_in_view_layer(Main *bmain, ViewLayer *view_layer, Object *ob)
+{
+ Base *base = BKE_view_layer_base_find(view_layer, ob);
+
+ if (!base) {
+ LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer);
+ BKE_collection_object_add(bmain, layer_collection->collection, ob);
+ base = BKE_view_layer_base_find(view_layer, ob);
+ }
+
+ return base;
+}
+
static int object_add_named_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -3379,7 +3488,8 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
Base *basen;
Object *ob;
- const bool linked = RNA_boolean_get(op->ptr, "linked");
+ const bool duplicate = RNA_boolean_get(op->ptr, "duplicate");
+ const bool linked = duplicate && RNA_boolean_get(op->ptr, "linked");
const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag;
char name[MAX_ID_NAME - 2];
@@ -3393,10 +3503,30 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
}
/* prepare dupli */
- basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag, 0);
+ if (duplicate) {
+ basen = object_add_duplicate_internal(
+ bmain,
+ scene,
+ view_layer,
+ ob,
+ dupflag,
+ /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this
+ * function will only work if the object is already linked in the view layer, which is not
+ * the case here. So we have to do the new-ID relinking ourselves
+ * (#copy_object_set_idnew()).
+ */
+ LIB_ID_DUPLICATE_IS_SUBPROCESS);
+ }
+ else {
+ /* basen is actually not a new base in this case. */
+ basen = object_add_ensure_in_view_layer(bmain, view_layer, ob);
+ }
if (basen == NULL) {
- BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated");
+ BKE_report(op->reports,
+ RPT_ERROR,
+ duplicate ? "Object could not be duplicated" :
+ "Object could not be linked to the view layer");
return OPERATOR_CANCELLED;
}
@@ -3410,7 +3540,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
/* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or
* BKE_view_layer_base_deselect_all(). */
- ED_object_base_deselect_all(view_layer, NULL, BA_DESELECT);
+ ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT);
ED_object_base_select(basen, BA_SELECT);
ED_object_base_activate(C, basen);
@@ -3443,11 +3573,24 @@ void OBJECT_OT_add_named(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ PropertyRNA *prop;
+
+ prop = RNA_def_boolean(
+ ot->srna,
+ "duplicate",
+ true,
+ "Duplicate",
+ "Create a duplicate of the object. If not set, only ensures the object is linked into the "
+ "active view layer, positions and selects/activates it (deselecting others)");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
RNA_def_boolean(ot->srna,
"linked",
- 0,
+ false,
"Linked",
- "Duplicate object but not object data, linking to the original data");
+ "Duplicate object but not object data, linking to the original data (ignored if "
+ "'duplicate' is false)");
+
RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add");
object_add_drop_xy_props(ot);
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index f651f5bc3fd..3370476d466 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -669,9 +669,11 @@ static void bake_targets_clear(Main *bmain, const bool is_tangent)
}
/* create new mesh with edit mode changes and modifiers applied */
-static Mesh *bake_mesh_new_from_object(Object *object)
+static Mesh *bake_mesh_new_from_object(Depsgraph *depsgraph,
+ Object *object,
+ const bool preserve_origindex)
{
- Mesh *me = BKE_mesh_new_from_object(NULL, object, false);
+ Mesh *me = BKE_mesh_new_from_object(depsgraph, object, false, preserve_origindex);
if (me->flag & ME_AUTOSMOOTH) {
BKE_mesh_split_faces(me, true);
@@ -961,10 +963,39 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re
return true;
}
+static int find_original_loop(const Mesh *me_orig,
+ const int *vert_origindex,
+ const int *poly_origindex,
+ const int poly_eval,
+ const int vert_eval)
+{
+ /* Get original vertex and polygon index. There is currently no loop mapping
+ * in modifier stack evaluation. */
+ const int vert_orig = vert_origindex[vert_eval];
+ const int poly_orig = poly_origindex[poly_eval];
+
+ if (vert_orig == ORIGINDEX_NONE || poly_orig == ORIGINDEX_NONE) {
+ return ORIGINDEX_NONE;
+ }
+
+ /* Find matching loop with original vertex in original polygon. */
+ MPoly *mpoly_orig = me_orig->mpoly + poly_orig;
+ MLoop *mloop_orig = me_orig->mloop + mpoly_orig->loopstart;
+ for (int j = 0; j < mpoly_orig->totloop; ++j, ++mloop_orig) {
+ if (mloop_orig->v == vert_orig) {
+ return mpoly_orig->loopstart + j;
+ }
+ }
+
+ return ORIGINDEX_NONE;
+}
+
static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets,
- Mesh *me,
+ Object *ob,
+ Mesh *me_eval,
BakePixel *pixel_array)
{
+ Mesh *me = ob->data;
const int num_pixels = targets->num_pixels;
/* Initialize blank pixels. */
@@ -983,16 +1014,31 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets,
}
/* Populate through adjacent triangles, first triangle wins. */
- const int tottri = poly_to_tri_count(me->totpoly, me->totloop);
+ const int tottri = poly_to_tri_count(me_eval->totpoly, me_eval->totloop);
MLoopTri *looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
- BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
+ BKE_mesh_recalc_looptri(
+ me_eval->mloop, me_eval->mpoly, me_eval->mvert, me_eval->totloop, me_eval->totpoly, looptri);
+
+ /* For mapping back to original mesh in case there are modifiers. */
+ const int *vert_origindex = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX);
+ const int *poly_origindex = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX);
for (int i = 0; i < tottri; i++) {
const MLoopTri *lt = &looptri[i];
for (int j = 0; j < 3; j++) {
- const unsigned int l = lt->tri[j];
+ unsigned int l = lt->tri[j];
+ unsigned int v = me_eval->mloop[l].v;
+
+ /* Map back to original loop if there are modifiers. */
+ if (vert_origindex != NULL && poly_origindex != NULL) {
+ l = find_original_loop(me, vert_origindex, poly_origindex, lt->poly, v);
+ if (l == ORIGINDEX_NONE || l >= me->totloop) {
+ continue;
+ }
+ }
+
BakePixel *pixel = &pixel_array[l];
if (pixel->primitive_id != -1) {
@@ -1004,7 +1050,7 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets,
/* Seed is the vertex, so that sampling noise is coherent for the same
* vertex, but different corners can still have different normals,
* materials and UVs. */
- pixel->seed = me->mloop[l].v;
+ pixel->seed = v;
/* Barycentric coordinates, nudged a bit to avoid precision issues that
* may happen when exactly at the vertex coordinate. */
@@ -1043,7 +1089,7 @@ static void bake_result_add_to_rgba(float rgba[4], const float *result, const in
}
}
-static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob, Mesh *me_split)
+static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob)
{
Mesh *me = ob->data;
MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
@@ -1052,11 +1098,6 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob,
const int num_channels = targets->num_channels;
const float *result = targets->result;
- /* We bake using a mesh with additional vertices for split normals, but the
- * number of loops must match to be able to transfer the vertex colors. */
- BLI_assert(me->totloop == me_split->totloop);
- UNUSED_VARS_NDEBUG(me_split);
-
if (mcol_valid) {
const int totvert = me->totvert;
const int totloop = me->totloop;
@@ -1111,16 +1152,17 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob,
static bool bake_targets_init(const BakeAPIRender *bkr,
BakeTargets *targets,
Object *ob,
+ Object *ob_eval,
ReportList *reports)
{
if (bkr->target == R_BAKE_TARGET_IMAGE_TEXTURES) {
if (bkr->save_mode == R_BAKE_SAVE_INTERNAL) {
- if (!bake_targets_init_internal(bkr, targets, ob, reports)) {
+ if (!bake_targets_init_internal(bkr, targets, ob_eval, reports)) {
return false;
}
}
else if (bkr->save_mode == R_BAKE_SAVE_EXTERNAL) {
- if (!bake_targets_init_external(bkr, targets, ob, reports)) {
+ if (!bake_targets_init_external(bkr, targets, ob_eval, reports)) {
return false;
}
}
@@ -1145,14 +1187,15 @@ static bool bake_targets_init(const BakeAPIRender *bkr,
static void bake_targets_populate_pixels(const BakeAPIRender *bkr,
BakeTargets *targets,
- Mesh *me,
+ Object *ob,
+ Mesh *me_eval,
BakePixel *pixel_array)
{
if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) {
- bake_targets_populate_pixels_vertex_colors(targets, me, pixel_array);
+ bake_targets_populate_pixels_vertex_colors(targets, ob, me_eval, pixel_array);
}
else {
- RE_bake_pixels_populate(me, pixel_array, targets->num_pixels, targets, bkr->uv_layer);
+ RE_bake_pixels_populate(me_eval, pixel_array, targets->num_pixels, targets, bkr->uv_layer);
}
}
@@ -1160,7 +1203,7 @@ static bool bake_targets_output(const BakeAPIRender *bkr,
BakeTargets *targets,
Object *ob,
Object *ob_eval,
- Mesh *me,
+ Mesh *me_eval,
BakePixel *pixel_array,
ReportList *reports)
{
@@ -1169,11 +1212,12 @@ static bool bake_targets_output(const BakeAPIRender *bkr,
return bake_targets_output_internal(bkr, targets, ob, pixel_array, reports);
}
if (bkr->save_mode == R_BAKE_SAVE_EXTERNAL) {
- return bake_targets_output_external(bkr, targets, ob, ob_eval, me, pixel_array, reports);
+ return bake_targets_output_external(
+ bkr, targets, ob, ob_eval, me_eval, pixel_array, reports);
}
}
else if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) {
- return bake_targets_output_vertex_colors(targets, ob, me);
+ return bake_targets_output_vertex_colors(targets, ob);
}
return false;
@@ -1213,8 +1257,8 @@ static int bake(const BakeAPIRender *bkr,
BakeHighPolyData *highpoly = NULL;
int tot_highpoly = 0;
- Mesh *me_low = NULL;
- Mesh *me_cage = NULL;
+ Mesh *me_low_eval = NULL;
+ Mesh *me_cage_eval = NULL;
MultiresModifierData *mmd_low = NULL;
int mmd_flags_low = 0;
@@ -1224,6 +1268,8 @@ static int bake(const BakeAPIRender *bkr,
BakeTargets targets = {NULL};
+ const bool preserve_origindex = (bkr->target == R_BAKE_TARGET_VERTEX_COLORS);
+
RE_bake_engine_set_engine_parameters(re, bmain, scene);
if (!RE_bake_has_engine(re)) {
@@ -1287,10 +1333,10 @@ static int bake(const BakeAPIRender *bkr,
ob_low_eval = DEG_get_evaluated_object(depsgraph, ob_low);
/* get the mesh as it arrives in the renderer */
- me_low = bake_mesh_new_from_object(ob_low_eval);
+ me_low_eval = bake_mesh_new_from_object(depsgraph, ob_low_eval, preserve_origindex);
/* Initialize bake targets. */
- if (!bake_targets_init(bkr, &targets, ob_low_eval, reports)) {
+ if (!bake_targets_init(bkr, &targets, ob_low, ob_low_eval, reports)) {
goto cleanup;
}
@@ -1298,7 +1344,7 @@ static int bake(const BakeAPIRender *bkr,
* it is populated later with the cage mesh (smoothed version of the mesh). */
pixel_array_low = MEM_mallocN(sizeof(BakePixel) * targets.num_pixels, "bake pixels low poly");
if ((bkr->is_selected_to_active && (ob_cage == NULL) && bkr->is_cage) == false) {
- bake_targets_populate_pixels(bkr, &targets, me_low, pixel_array_low);
+ bake_targets_populate_pixels(bkr, &targets, ob_low, me_low_eval, pixel_array_low);
}
if (bkr->is_selected_to_active) {
@@ -1307,8 +1353,9 @@ static int bake(const BakeAPIRender *bkr,
/* prepare cage mesh */
if (ob_cage) {
- me_cage = bake_mesh_new_from_object(ob_cage_eval);
- if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) {
+ me_cage_eval = bake_mesh_new_from_object(depsgraph, ob_cage_eval, preserve_origindex);
+ if ((me_low_eval->totpoly != me_cage_eval->totpoly) ||
+ (me_low_eval->totloop != me_cage_eval->totloop)) {
BKE_report(reports,
RPT_ERROR,
"Invalid cage object, the cage mesh must have the same number "
@@ -1348,8 +1395,8 @@ static int bake(const BakeAPIRender *bkr,
BKE_object_handle_data_update(depsgraph, scene, ob_low_eval);
}
- me_cage = BKE_mesh_new_from_object(NULL, ob_low_eval, false);
- bake_targets_populate_pixels(bkr, &targets, me_cage, pixel_array_low);
+ me_cage_eval = BKE_mesh_new_from_object(NULL, ob_low_eval, false, preserve_origindex);
+ bake_targets_populate_pixels(bkr, &targets, ob_low, me_cage_eval, pixel_array_low);
}
highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects");
@@ -1367,7 +1414,7 @@ static int bake(const BakeAPIRender *bkr,
highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter);
highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER;
highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
- highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false);
+ highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false, false);
/* Low-poly to high-poly transformation matrix. */
copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat);
@@ -1391,7 +1438,7 @@ static int bake(const BakeAPIRender *bkr,
pixel_array_high = MEM_mallocN(sizeof(BakePixel) * targets.num_pixels,
"bake pixels high poly");
- if (!RE_bake_pixels_populate_from_objects(me_low,
+ if (!RE_bake_pixels_populate_from_objects(me_low_eval,
pixel_array_low,
pixel_array_high,
highpoly,
@@ -1402,7 +1449,7 @@ static int bake(const BakeAPIRender *bkr,
bkr->max_ray_distance,
ob_low_eval->obmat,
(ob_cage ? ob_cage->obmat : ob_low_eval->obmat),
- me_cage)) {
+ me_cage_eval)) {
BKE_report(reports, RPT_ERROR, "Error handling selected objects");
goto cleanup;
}
@@ -1478,7 +1525,7 @@ static int bake(const BakeAPIRender *bkr,
targets.num_pixels,
targets.num_channels,
targets.result,
- me_low,
+ me_low_eval,
bkr->normal_swizzle,
ob_low_eval->obmat);
}
@@ -1497,8 +1544,8 @@ static int bake(const BakeAPIRender *bkr,
}
/* Evaluate modifiers again. */
- me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false);
- bake_targets_populate_pixels(bkr, &targets, me_nores, pixel_array_low);
+ me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false, false);
+ bake_targets_populate_pixels(bkr, &targets, ob_low, me_nores, pixel_array_low);
RE_bake_normal_world_to_tangent(pixel_array_low,
targets.num_pixels,
@@ -1527,7 +1574,7 @@ static int bake(const BakeAPIRender *bkr,
else {
/* save the results */
if (bake_targets_output(
- bkr, &targets, ob_low, ob_low_eval, me_low, pixel_array_low, reports)) {
+ bkr, &targets, ob_low, ob_low_eval, me_low_eval, pixel_array_low, reports)) {
op_result = OPERATOR_FINISHED;
}
else {
@@ -1562,12 +1609,12 @@ cleanup:
bake_targets_free(&targets);
- if (me_low != NULL) {
- BKE_id_free(NULL, &me_low->id);
+ if (me_low_eval != NULL) {
+ BKE_id_free(NULL, &me_low_eval->id);
}
- if (me_cage != NULL) {
- BKE_id_free(NULL, &me_cage->id);
+ if (me_cage_eval != NULL) {
+ BKE_id_free(NULL, &me_cage_eval->id);
}
DEG_graph_free(depsgraph);
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 72ef72403cf..22c9d669ff3 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -684,7 +684,8 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot)
false,
"Auto Transform",
"Automatically compute transformation to get the best possible match between source and "
- "destination meshes (WARNING: results will never be as good as manual matching of objects)");
+ "destination meshes.\n"
+ "Warning: Results will never be as good as manual matching of objects");
RNA_def_boolean(ot->srna,
"use_object_transform",
true,
diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c
index 72dde7b734b..6e3a5e715f6 100644
--- a/source/blender/editors/object/object_data_transform.c
+++ b/source/blender/editors/object/object_data_transform.c
@@ -593,13 +593,14 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float
if (xod_base->is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
nurb = &editnurb->nurbs;
- BKE_curve_nurbs_vert_coords_apply_with_mat4(&editnurb->nurbs, xod->elem_array, mat, true);
+ BKE_curve_nurbs_vert_coords_apply_with_mat4(
+ &editnurb->nurbs, xod->elem_array, mat, CU_IS_2D(cu));
/* Always operate on all keys for the moment. */
// key_index = editnurb->shapenr - 1;
}
else {
nurb = &cu->nurb;
- BKE_curve_nurbs_vert_coords_apply_with_mat4(&cu->nurb, xod->elem_array, mat, true);
+ BKE_curve_nurbs_vert_coords_apply_with_mat4(&cu->nurb, xod->elem_array, mat, CU_IS_2D(cu));
}
if ((key != NULL) && (xod->key_data != NULL)) {
@@ -694,12 +695,12 @@ void ED_object_data_xform_restore(struct XFormObjectData *xod_base)
struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base;
if (xod_base->is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
- BKE_curve_nurbs_vert_coords_apply(&editnurb->nurbs, xod->elem_array, true);
+ BKE_curve_nurbs_vert_coords_apply(&editnurb->nurbs, xod->elem_array, CU_IS_2D(cu));
/* Always operate on all keys for the moment. */
// key_index = editnurb->shapenr - 1;
}
else {
- BKE_curve_nurbs_vert_coords_apply(&cu->nurb, xod->elem_array, true);
+ BKE_curve_nurbs_vert_coords_apply(&cu->nurb, xod->elem_array, CU_IS_2D(cu));
}
if ((key != NULL) && (xod->key_data != NULL)) {
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index c774bc9f9cc..5be572baec5 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -55,6 +55,7 @@
#include "IMB_imbuf_types.h"
#include "BKE_anim_visualization.h"
+#include "BKE_armature.h"
#include "BKE_collection.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
@@ -520,11 +521,18 @@ static bool mesh_needs_keyindex(Main *bmain, const Mesh *me)
}
/**
- * Load EditMode data back into the object,
- * optionally freeing the editmode data.
+ * Load edit-mode data back into the object.
+ *
+ * \param load_data: Flush the edit-mode data back to the object.
+ * \param free_data: Free the edit-mode data.
*/
-static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool freedata)
+static bool ED_object_editmode_load_free_ex(Main *bmain,
+ Object *obedit,
+ const bool load_data,
+ const bool free_data)
{
+ BLI_assert(load_data || free_data);
+
if (obedit == NULL) {
return false;
}
@@ -544,9 +552,11 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
return false;
}
- EDBM_mesh_load_ex(bmain, obedit, freedata);
+ if (load_data) {
+ EDBM_mesh_load_ex(bmain, obedit, free_data);
+ }
- if (freedata) {
+ if (free_data) {
EDBM_mesh_free(me->edit_mesh);
MEM_freeN(me->edit_mesh);
me->edit_mesh = NULL;
@@ -562,9 +572,21 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (arm->edbo == NULL) {
return false;
}
- ED_armature_from_edit(bmain, obedit->data);
- if (freedata) {
+
+ if (load_data) {
+ ED_armature_from_edit(bmain, obedit->data);
+ }
+
+ if (free_data) {
ED_armature_edit_free(obedit->data);
+
+ if (load_data == false) {
+ /* Don't keep unused pose channels created by duplicating bones
+ * which may have been deleted/undone, see: T87631. */
+ if (obedit->pose != NULL) {
+ BKE_pose_channels_clear_with_null_bone(obedit->pose, true);
+ }
+ }
}
/* TODO(sergey): Pose channels might have been changed, so need
* to inform dependency graph about this. But is it really the
@@ -577,8 +599,12 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (cu->editnurb == NULL) {
return false;
}
- ED_curve_editnurb_load(bmain, obedit);
- if (freedata) {
+
+ if (load_data) {
+ ED_curve_editnurb_load(bmain, obedit);
+ }
+
+ if (free_data) {
ED_curve_editnurb_free(obedit);
}
}
@@ -587,8 +613,12 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (cu->editfont == NULL) {
return false;
}
- ED_curve_editfont_load(obedit);
- if (freedata) {
+
+ if (load_data) {
+ ED_curve_editfont_load(obedit);
+ }
+
+ if (free_data) {
ED_curve_editfont_free(obedit);
}
}
@@ -597,8 +627,12 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (lt->editlatt == NULL) {
return false;
}
- BKE_editlattice_load(obedit);
- if (freedata) {
+
+ if (load_data) {
+ BKE_editlattice_load(obedit);
+ }
+
+ if (free_data) {
BKE_editlattice_free(obedit);
}
}
@@ -607,8 +641,12 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (mb->editelems == NULL) {
return false;
}
- ED_mball_editmball_load(obedit);
- if (freedata) {
+
+ if (load_data) {
+ ED_mball_editmball_load(obedit);
+ }
+
+ if (free_data) {
ED_mball_editmball_free(obedit);
}
}
@@ -616,9 +654,11 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
return false;
}
- char *needs_flush_ptr = BKE_object_data_editmode_flush_ptr_get(obedit->data);
- if (needs_flush_ptr) {
- *needs_flush_ptr = false;
+ if (load_data) {
+ char *needs_flush_ptr = BKE_object_data_editmode_flush_ptr_get(obedit->data);
+ if (needs_flush_ptr) {
+ *needs_flush_ptr = false;
+ }
}
return true;
@@ -626,7 +666,7 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
bool ED_object_editmode_load(Main *bmain, Object *obedit)
{
- return ED_object_editmode_load_ex(bmain, obedit, false);
+ return ED_object_editmode_load_free_ex(bmain, obedit, true, false);
}
/**
@@ -635,9 +675,9 @@ bool ED_object_editmode_load(Main *bmain, Object *obedit)
*/
bool ED_object_editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int flag)
{
- const bool freedata = (flag & EM_FREEDATA) != 0;
+ const bool free_data = (flag & EM_FREEDATA) != 0;
- if (ED_object_editmode_load_ex(bmain, obedit, freedata) == false) {
+ if (ED_object_editmode_load_free_ex(bmain, obedit, true, free_data) == false) {
/* in rare cases (background mode) its possible active object
* is flagged for editmode, without 'obedit' being set T35489. */
if (UNLIKELY(obedit && obedit->mode & OB_MODE_EDIT)) {
@@ -648,8 +688,8 @@ bool ED_object_editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int f
return true;
}
- /* freedata only 0 now on file saves and render */
- if (freedata) {
+ /* `free_data` only false now on file saves and render. */
+ if (free_data) {
/* flag object caches as outdated */
ListBase pidlist;
BKE_ptcache_ids_from_object(&pidlist, obedit, scene, 0);
@@ -683,6 +723,16 @@ bool ED_object_editmode_exit(bContext *C, int flag)
return ED_object_editmode_exit_ex(bmain, scene, obedit, flag);
}
+/**
+ * Support freeing edit-mode data without flushing it back to the object.
+ *
+ * \return true if data was freed.
+ */
+bool ED_object_editmode_free_ex(Main *bmain, Object *obedit)
+{
+ return ED_object_editmode_load_free_ex(bmain, obedit, false, true);
+}
+
bool ED_object_editmode_exit_multi_ex(Main *bmain, Scene *scene, ViewLayer *view_layer, int flag)
{
Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
@@ -718,26 +768,22 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag
return false;
}
+ /* This checks actual `ob->data`, for cases when other scenes have it in edit-mode context.
+ * Currently multiple objects sharing a mesh being in edit-mode at once isn't supported,
+ * see: T86767. */
+ if (BKE_object_is_in_editmode(ob)) {
+ return true;
+ }
+
if (BKE_object_obdata_is_libdata(ob)) {
/* Ideally the caller should check this. */
CLOG_WARN(&LOG, "Unable to enter edit-mode on library data for object '%s'", ob->id.name + 2);
return false;
}
- if ((ob->mode & OB_MODE_EDIT) == 0) {
- ob->restore_mode = ob->mode;
+ ob->restore_mode = ob->mode;
- ob->mode = OB_MODE_EDIT;
- }
-
- /* This checks actual `object->data`,
- * for cases when other scenes have it in edit-mode context.
- *
- * It's important to run this after setting the object's mode (above), since in rare cases
- * the object may have the edit-data but not it's object-mode set. See T85974. */
- if (BKE_object_is_in_editmode(ob)) {
- return true;
- }
+ ob->mode = OB_MODE_EDIT;
if (ob->type == OB_MESH) {
ok = true;
@@ -1544,30 +1590,10 @@ static const EnumPropertyItem *object_mode_set_itemsf(bContext *C,
return rna_enum_object_mode_items;
}
- Object *ob = CTX_data_active_object(C);
+ const Object *ob = CTX_data_active_object(C);
if (ob) {
- const bool use_mode_particle_edit = (BLI_listbase_is_empty(&ob->particlesystem) == false) ||
- (ob->soft != NULL) ||
- (BKE_modifiers_findby_type(ob, eModifierType_Cloth) !=
- NULL);
while (input->identifier) {
- if ((input->value == OB_MODE_EDIT && OB_TYPE_SUPPORT_EDITMODE(ob->type)) ||
- (input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) ||
- (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) ||
- (ELEM(input->value,
- OB_MODE_SCULPT,
- OB_MODE_VERTEX_PAINT,
- OB_MODE_WEIGHT_PAINT,
- OB_MODE_TEXTURE_PAINT) &&
- (ob->type == OB_MESH)) ||
- (ELEM(input->value,
- OB_MODE_EDIT_GPENCIL,
- OB_MODE_PAINT_GPENCIL,
- OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL,
- OB_MODE_VERTEX_GPENCIL) &&
- (ob->type == OB_GPENCIL)) ||
- (input->value == OB_MODE_OBJECT)) {
+ if (ED_object_mode_compat_test(ob, input->value)) {
RNA_enum_item_add(&item, &totitem, input);
}
input++;
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 00426e96a81..5bf04e195fe 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -90,7 +90,7 @@ void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot);
void OBJECT_OT_move_to_collection(struct wmOperatorType *ot);
void OBJECT_OT_link_to_collection(struct wmOperatorType *ot);
-void OBJECT_OT_switch_object(struct wmOperatorType *ot);
+void OBJECT_OT_transfer_mode(struct wmOperatorType *ot);
/* object_select.c */
void OBJECT_OT_select_all(struct wmOperatorType *ot);
@@ -157,10 +157,6 @@ bool edit_modifier_poll_generic(struct bContext *C,
const bool is_liboverride_allowed);
void edit_modifier_properties(struct wmOperatorType *ot);
bool edit_modifier_invoke_properties(struct bContext *C, struct wmOperator *op);
-bool edit_modifier_invoke_properties_with_hover_no_active(struct bContext *C,
- struct wmOperator *op,
- const struct wmEvent *event,
- int *r_retval);
struct ModifierData *edit_modifier_property_get(struct wmOperator *op,
struct Object *ob,
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index 99103cf10c7..ed06cd2a217 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -30,6 +30,10 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "PIL_time.h"
+
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_layer.h"
@@ -39,11 +43,13 @@
#include "BKE_paint.h"
#include "BKE_report.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
+#include "RNA_define.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -111,43 +117,46 @@ static const char *object_mode_op_string(eObjectMode mode)
*/
bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
{
- if (ob) {
- if (mode == OB_MODE_OBJECT) {
- return true;
- }
+ if (mode == OB_MODE_OBJECT) {
+ return true;
+ }
- switch (ob->type) {
- case OB_MESH:
- if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
- OB_MODE_TEXTURE_PAINT | OB_MODE_PARTICLE_EDIT)) {
- return true;
- }
- break;
- case OB_CURVE:
- case OB_SURF:
- case OB_FONT:
- case OB_MBALL:
- if (mode & OB_MODE_EDIT) {
- return true;
- }
- break;
- case OB_LATTICE:
- if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) {
- return true;
- }
- break;
- case OB_ARMATURE:
- if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) {
- return true;
- }
- break;
- case OB_GPENCIL:
- if (mode & (OB_MODE_EDIT | OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL |
- OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) {
+ switch (ob->type) {
+ case OB_MESH:
+ if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
+ OB_MODE_TEXTURE_PAINT)) {
+ return true;
+ }
+ if (mode & OB_MODE_PARTICLE_EDIT) {
+ if (ED_object_particle_edit_mode_supported(ob)) {
return true;
}
- break;
- }
+ }
+ break;
+ case OB_CURVE:
+ case OB_SURF:
+ case OB_FONT:
+ case OB_MBALL:
+ if (mode & OB_MODE_EDIT) {
+ return true;
+ }
+ break;
+ case OB_LATTICE:
+ if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) {
+ return true;
+ }
+ break;
+ case OB_ARMATURE:
+ if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) {
+ return true;
+ }
+ break;
+ case OB_GPENCIL:
+ if (mode & (OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | OB_MODE_SCULPT_GPENCIL |
+ OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) {
+ return true;
+ }
+ break;
}
return false;
@@ -400,51 +409,66 @@ bool ED_object_mode_generic_has_data(struct Depsgraph *depsgraph, struct Object
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Switch Object
+/** \name Transfer Mode
*
* Enters the same mode of the current active object in another object,
* leaving the mode of the current object.
* \{ */
-static bool object_switch_object_poll(bContext *C)
+static bool object_transfer_mode_poll(bContext *C)
{
-
- if (!U.experimental.use_switch_object_operator) {
- return false;
- }
-
if (!CTX_wm_region_view3d(C)) {
return false;
}
const Object *ob = CTX_data_active_object(C);
- return ob && (ob->mode & (OB_MODE_EDIT | OB_MODE_SCULPT));
+ return ob && (ob->mode != OB_MODE_OBJECT);
}
-static int object_switch_object_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+/* Update the viewport rotation origin to the mouse cursor. */
+static void object_transfer_mode_reposition_view_pivot(bContext *C, const int mval[2])
{
ARegion *region = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
+ float global_loc[3];
+ if (!ED_view3d_autodist_simple(region, mval, global_loc, 0, NULL)) {
+ return;
+ }
+ UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
+ copy_v3_v3(ups->average_stroke_accum, global_loc);
+ ups->average_stroke_counter = 1;
+ ups->last_stroke_valid = true;
+}
+
+static void object_overlay_mode_transfer_animation_start(bContext *C, Object *ob_dst)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *ob_dst_eval = DEG_get_evaluated_object(depsgraph, ob_dst);
+ ob_dst_eval->runtime.overlay_mode_transfer_start_time = PIL_check_seconds_timer();
+}
+
+static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base_dst)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
if (base_dst == NULL) {
- return OPERATOR_CANCELLED;
+ return false;
}
Object *ob_dst = base_dst->object;
Object *ob_src = CTX_data_active_object(C);
if (ob_dst == ob_src) {
- return OPERATOR_CANCELLED;
+ return false;
}
const eObjectMode last_mode = (eObjectMode)ob_src->mode;
if (!ED_object_mode_compat_test(ob_dst, last_mode)) {
- return OPERATOR_CANCELLED;
+ return false;
}
- int retval = OPERATOR_CANCELLED;
+ bool mode_transfered = false;
ED_undo_group_begin(C);
@@ -460,42 +484,108 @@ static int object_switch_object_invoke(bContext *C, wmOperator *op, const wmEven
ob_dst_orig = DEG_get_original_object(ob_dst);
ED_object_mode_set_ex(C, last_mode, true, op->reports);
- /* Update the viewport rotation origin to the mouse cursor. */
- if (last_mode & OB_MODE_ALL_PAINT) {
- float global_loc[3];
- if (ED_view3d_autodist_simple(region, event->mval, global_loc, 0, NULL)) {
- UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
- copy_v3_v3(ups->average_stroke_accum, global_loc);
- ups->average_stroke_counter = 1;
- ups->last_stroke_valid = true;
- }
+ if (RNA_boolean_get(op->ptr, "use_flash_on_transfer")) {
+ object_overlay_mode_transfer_animation_start(C, ob_dst);
}
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_toolsystem_update_from_context_view3d(C);
- retval = OPERATOR_FINISHED;
+ mode_transfered = true;
}
ED_undo_group_end(C);
+ return mode_transfered;
+}
+
+static int object_transfer_mode_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ switch (event->type) {
+ case LEFTMOUSE:
+ if (event->val == KM_PRESS) {
+ WM_cursor_modal_restore(CTX_wm_window(C));
+ ED_workspace_status_text(C, NULL);
+
+ /* This ensures that the click was done in an viewport region. */
+ bScreen *screen = CTX_wm_screen(C);
+ ARegion *region = BKE_screen_find_main_region_at_xy(
+ screen, SPACE_VIEW3D, event->x, event->y);
+ if (!region) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const int mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin};
+ Base *base_dst = ED_view3d_give_base_under_cursor(C, mval);
+ const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst);
+ if (!mode_transfered) {
+ return OPERATOR_CANCELLED;
+ }
- return retval;
+ return OPERATOR_FINISHED;
+ }
+ break;
+ case RIGHTMOUSE: {
+ WM_cursor_modal_restore(CTX_wm_window(C));
+ ED_workspace_status_text(C, NULL);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ return OPERATOR_RUNNING_MODAL;
}
-void OBJECT_OT_switch_object(wmOperatorType *ot)
+static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const bool use_eyedropper = RNA_boolean_get(op->ptr, "use_eyedropper");
+ if (use_eyedropper) {
+ ED_workspace_status_text(C, TIP_("Click in the viewport to select an object"));
+ WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER);
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ Object *ob_src = CTX_data_active_object(C);
+ const eObjectMode src_mode = (eObjectMode)ob_src->mode;
+
+ Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
+ const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst);
+ if (!mode_transfered) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (src_mode & OB_MODE_ALL_PAINT) {
+ object_transfer_mode_reposition_view_pivot(C, event->mval);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_transfer_mode(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Switch Object";
- ot->idname = "OBJECT_OT_switch_object";
+ ot->name = "Transfer Mode";
+ ot->idname = "OBJECT_OT_transfer_mode";
ot->description =
"Switches the active object and assigns the same mode to a new one under the mouse cursor, "
"leaving the active mode in the current one";
/* api callbacks */
- ot->invoke = object_switch_object_invoke;
- ot->poll = object_switch_object_poll;
+ ot->invoke = object_transfer_mode_invoke;
+ ot->modal = object_transfer_mode_modal;
+ ot->poll = object_transfer_mode_poll;
/* Undo push is handled by the operator. */
ot->flag = OPTYPE_REGISTER;
+
+ RNA_def_boolean(ot->srna,
+ "use_eyedropper",
+ false,
+ "Use Eyedropper",
+ "Pick the object to switch to using an eyedropper");
+
+ RNA_def_boolean(ot->srna,
+ "use_flash_on_transfer",
+ true,
+ "Flash On Transfer",
+ "Flash the target object when transfering the mode");
}
/** \} */
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 7673649c261..e14e5cbd44b 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -63,6 +63,7 @@
#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
@@ -210,7 +211,7 @@ ModifierData *ED_object_modifier_add(
/* special cases */
if (type == eModifierType_Softbody) {
if (!ob->soft) {
- ob->soft = sbNew(scene);
+ ob->soft = sbNew();
ob->softflag |= OB_SB_GOAL | OB_SB_EDGES;
}
}
@@ -772,6 +773,8 @@ static bool modifier_apply_obdata(
return false;
}
+ Main *bmain = DEG_get_bmain(depsgraph);
+ BKE_object_material_from_eval_data(bmain, ob, &mesh_applied->id);
BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true);
if (md_eval->type == eModifierType_Multires) {
@@ -1115,6 +1118,47 @@ bool edit_modifier_invoke_properties(bContext *C, wmOperator *op)
return false;
}
+/**
+ * If the "modifier" property is not set, fill the modifier property with the name of the modifier
+ * with a UI panel below the mouse cursor, unless a specific modifier is set with a context
+ * pointer. Used in order to apply modifier operators on hover over their panels.
+ */
+static bool edit_modifier_invoke_properties_with_hover(bContext *C,
+ wmOperator *op,
+ const wmEvent *event,
+ int *r_retval)
+{
+ if (RNA_struct_property_is_set(op->ptr, "modifier")) {
+ return true;
+ }
+
+ /* Note that the context pointer is *not* the active modifier, it is set in UI layouts. */
+ PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
+ if (ctx_ptr.data != NULL) {
+ ModifierData *md = ctx_ptr.data;
+ RNA_string_set(op->ptr, "modifier", md->name);
+ return true;
+ }
+
+ PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
+ if (panel_ptr == NULL || RNA_pointer_is_null(panel_ptr)) {
+ *r_retval = OPERATOR_CANCELLED;
+ return false;
+ }
+
+ if (!RNA_struct_is_a(panel_ptr->type, &RNA_Modifier)) {
+ /* Work around multiple operators using the same shortcut. The operators for the other
+ * stacks in the property editor use the same key, and will not run after these return
+ * OPERATOR_CANCELLED. */
+ *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return false;
+ }
+
+ const ModifierData *md = panel_ptr->data;
+ RNA_string_set(op->ptr, "modifier", md->name);
+ return true;
+}
+
ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type)
{
char modifier_name[MAX_NAME];
@@ -1174,14 +1218,13 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_remove_exec(C, op);
}
-
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_remove(wmOperatorType *ot)
@@ -1221,13 +1264,13 @@ static int modifier_move_up_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_move_up_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_move_up(wmOperatorType *ot)
@@ -1266,13 +1309,13 @@ static int modifier_move_down_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_move_down_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_move_down(wmOperatorType *ot)
@@ -1309,12 +1352,13 @@ static int modifier_move_to_index_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_move_to_index_exec(C, op);
}
- return OPERATOR_CANCELLED;
+ return retval;
}
void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot)
@@ -1421,13 +1465,13 @@ static int modifier_apply_exec(bContext *C, wmOperator *op)
return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_DATA, false);
}
-static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_apply_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_apply(wmOperatorType *ot)
@@ -1465,15 +1509,13 @@ static int modifier_apply_as_shapekey_exec(bContext *C, wmOperator *op)
return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_SHAPE, keep);
}
-static int modifier_apply_as_shapekey_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event))
+static int modifier_apply_as_shapekey_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_apply_as_shapekey_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED(C),
@@ -1579,13 +1621,13 @@ static int modifier_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_copy_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_copy(wmOperatorType *ot)
@@ -1622,54 +1664,12 @@ static int modifier_set_active_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-/**
- * Get the modifier below the mouse cursor modifier without checking the context pointer.
- * Used in order to set the active modifier on mouse click. If this checked the context
- * pointer then it would always set the active modifier to the already active modifier.
- *
- * \param event: If this isn't NULL, the operator will also look for panels underneath
- * the cursor with custom-data set to a modifier.
- * \param r_retval: This should be used if #event is used in order to return
- * #OPERATOR_PASS_THROUGH to check other operators with the same key set.
- */
-bool edit_modifier_invoke_properties_with_hover_no_active(bContext *C,
- wmOperator *op,
- const wmEvent *event,
- int *r_retval)
-{
- if (RNA_struct_property_is_set(op->ptr, "modifier")) {
- return true;
- }
-
- PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
-
- if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) {
- if (RNA_struct_is_a(panel_ptr->type, &RNA_Modifier)) {
- ModifierData *md = panel_ptr->data;
- RNA_string_set(op->ptr, "modifier", md->name);
- return true;
- }
- BLI_assert(r_retval != NULL); /* We need the return value in this case. */
- if (r_retval != NULL) {
- *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
- }
- return false;
- }
-
- if (r_retval != NULL) {
- *r_retval = OPERATOR_CANCELLED;
- }
-
- return false;
-}
-
static int modifier_set_active_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
- if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) {
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_set_active_exec(C, op);
}
-
return retval;
}
@@ -1756,15 +1756,13 @@ static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_copy_to_selected_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event))
+static int modifier_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_copy_to_selected_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
static bool modifier_copy_to_selected_poll(bContext *C)
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 71bea6bf9e3..b50fc3c88fd 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -65,7 +65,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_paths_range_update);
WM_operatortype_append(OBJECT_OT_forcefield_toggle);
- WM_operatortype_append(OBJECT_OT_switch_object);
+ WM_operatortype_append(OBJECT_OT_transfer_mode);
WM_operatortype_append(OBJECT_OT_parent_set);
WM_operatortype_append(OBJECT_OT_parent_no_inverse_set);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 0aa739c2fc8..f3433833b5f 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -801,7 +801,7 @@ bool ED_object_parent_set(ReportList *reports,
* so we check this by assuming that the parent is selected too.
*/
/* XXX currently this should only happen for meshes, curves, surfaces,
- * and lattices - this stuff isn't available for metas yet */
+ * and lattices - this stuff isn't available for meta-balls yet. */
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
ModifierData *md;
@@ -812,7 +812,8 @@ bool ED_object_parent_set(ReportList *reports,
if (md) {
((CurveModifierData *)md)->object = par;
}
- if (par->runtime.curve_cache && par->runtime.curve_cache->path == NULL) {
+ if (par->runtime.curve_cache &&
+ par->runtime.curve_cache->anim_path_accum_length == NULL) {
DEG_id_tag_update(&par->id, ID_RECALC_GEOMETRY);
}
}
@@ -1943,7 +1944,7 @@ void ED_object_single_user(Main *bmain, Scene *scene, Object *ob)
ob->flag |= OB_DONE;
single_object_users(bmain, scene, NULL, OB_DONE, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
}
static void single_obdata_users(
@@ -2453,7 +2454,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
const bool success = BKE_lib_override_library_create(
- bmain, scene, view_layer, id_root, &obact->id);
+ bmain, scene, view_layer, id_root, &obact->id, NULL);
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
@@ -2569,14 +2570,14 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *op)
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
- if (success && is_override_instancing_object) {
+ if (is_override_instancing_object) {
ED_object_base_free_and_unlink(bmain, scene, ob_proxy_group);
}
DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_WINDOW, NULL);
- return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ return OPERATOR_FINISHED;
}
void OBJECT_OT_convert_proxy_to_override(wmOperatorType *ot)
@@ -2643,7 +2644,7 @@ static int make_single_user_exec(bContext *C, wmOperator *op)
single_object_action_users(bmain, scene, view_layer, v3d, flag);
}
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
WM_event_add_notifier(C, NC_WINDOW, NULL);
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
index 9ef2cce875f..1ff576504ce 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -525,7 +525,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
float d_a[3], d_b[3];
float d_a_proj[2], d_b_proj[2];
- float preview_plane_proj[4][3];
+ float preview_plane_proj[4][2];
const float y_axis_proj[2] = {0.0f, 1.0f};
mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]);
@@ -534,7 +534,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
for (int i = 0; i < 4; i++) {
float preview_plane_world_space[3];
mul_v3_m4v3(preview_plane_world_space, active_object->obmat, cd->preview_plane[i]);
- ED_view3d_project(region, preview_plane_world_space, preview_plane_proj[i]);
+ ED_view3d_project_v2(region, preview_plane_world_space, preview_plane_proj[i]);
}
/* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 13c0740bce6..b9a3bc87e19 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -1654,7 +1654,7 @@ static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *x
if (center_tot) {
mul_v3_fl(center, 1.0f / center_tot);
float center_proj[3];
- ED_view3d_project(xfd->vc.region, center, center_proj);
+ ED_view3d_project_v3(xfd->vc.region, center, center_proj);
xfd->prev.depth = center_proj[2];
xfd->prev.is_depth_valid = true;
}
@@ -1782,12 +1782,7 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons
vc.v3d->flag2 |= V3D_HIDE_OVERLAYS;
#endif
- ED_view3d_autodist_init(vc.depsgraph, vc.region, vc.v3d, 0);
-
- if (vc.rv3d->depths != NULL) {
- vc.rv3d->depths->damaged = true;
- }
- ED_view3d_depth_update(vc.region);
+ ED_view3d_depth_override(vc.depsgraph, vc.region, vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, true);
#ifdef USE_RENDER_OVERRIDE
vc.v3d->flag2 = flag2_prev;
@@ -1870,30 +1865,32 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
if (event->type == MOUSEMOVE || is_translate_init) {
const ViewDepths *depths = xfd->vc.rv3d->depths;
if (depths && ((uint)event->mval[0] < depths->w) && ((uint)event->mval[1] < depths->h)) {
- double depth = (double)ED_view3d_depth_read_cached(&xfd->vc, event->mval);
+ float depth_fl = 1.0f;
+ ED_view3d_depth_read_cached(depths, event->mval, 0, &depth_fl);
float location_world[3];
- if (depth == 1.0f) {
+ if (depth_fl == 1.0f) {
if (xfd->prev.is_depth_valid) {
- depth = (double)xfd->prev.depth;
+ depth_fl = xfd->prev.depth;
}
}
#ifdef USE_FAKE_DEPTH_INIT
/* First time only. */
- if (depth == 1.0f) {
+ if (depth_fl == 1.0f) {
if (xfd->prev.is_depth_valid == false) {
object_transform_axis_target_calc_depth_init(xfd, event->mval);
if (xfd->prev.is_depth_valid) {
- depth = (double)xfd->prev.depth;
+ depth_fl = xfd->prev.depth;
}
}
}
#endif
+ double depth = (double)depth_fl;
if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
- xfd->prev.depth = depth;
+ xfd->prev.depth = depth_fl;
xfd->prev.is_depth_valid = true;
- if (ED_view3d_depth_unproject(region, event->mval, depth, location_world)) {
+ if (ED_view3d_depth_unproject_v3(region, event->mval, depth, location_world)) {
if (is_translate) {
float normal[3];
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 2296e158cc8..3f40d637188 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -1751,7 +1751,7 @@ static bool *vgroup_selected_get(Object *ob)
/* Mirror the selection if X Mirror is enabled. */
Mesh *me = BKE_mesh_from_object(ob);
- if (me && (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) != 0) {
+ if (me && ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
BKE_object_defgroup_mirror_selection(ob, defbase_tot, mask, mask, &sel_count);
}
}
@@ -3985,6 +3985,10 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return DummyRNA_NULL_items;
+ }
+
Object *ob = ED_object_context(C);
EnumPropertyItem tmp = {0, "", 0, "", ""};
EnumPropertyItem *item = NULL;
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 0ec8238f741..97994b65f40 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -524,15 +524,13 @@ static void PE_set_view3d_data(bContext *C, PEData *data)
ED_view3d_viewcontext_init(C, &data->vc, data->depsgraph);
if (!XRAY_ENABLED(data->vc.v3d)) {
- if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
- /* needed or else the draw matrix can be incorrect */
- view3d_operator_needs_opengl(C);
-
- ED_view3d_backbuf_depth_validate(&data->vc);
- /* we may need to force an update here by setting the rv3d as dirty
- * for now it seems ok, but take care!:
- * rv3d->depths->dirty = 1; */
- ED_view3d_depth_update(data->vc.region);
+ if (!(data->vc.v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN)) {
+ ED_view3d_depth_override(data->depsgraph,
+ data->vc.region,
+ data->vc.v3d,
+ data->vc.obact,
+ V3D_DEPTH_OBJECT_ONLY,
+ true);
}
}
}
@@ -611,7 +609,7 @@ static bool key_test_depth(const PEData *data, const float co[3], const int scre
}
float win[3];
- ED_view3d_project(data->vc.region, co, win);
+ ED_view3d_project_v3(data->vc.region, co, win);
if (win[2] - 0.00001f > depth) {
return 0;
@@ -5377,8 +5375,7 @@ static bool particle_edit_toggle_poll(bContext *C)
return 0;
}
- return (ob->particlesystem.first || BKE_modifiers_findby_type(ob, eModifierType_Cloth) ||
- BKE_modifiers_findby_type(ob, eModifierType_Softbody));
+ return ED_object_particle_edit_mode_supported(ob);
}
static void free_all_psys_edit(Object *object)
@@ -5393,6 +5390,12 @@ static void free_all_psys_edit(Object *object)
}
}
+bool ED_object_particle_edit_mode_supported(const Object *ob)
+{
+ return (ob->particlesystem.first || BKE_modifiers_findby_type(ob, eModifierType_Cloth) ||
+ BKE_modifiers_findby_type(ob, eModifierType_Softbody));
+}
+
void ED_object_particle_edit_mode_enter_ex(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
/* Needed so #ParticleSystemModifierData.mesh_final is set. */
diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c
index 81a8b57776b..fca3b5817b0 100644
--- a/source/blender/editors/physics/rigidbody_object.c
+++ b/source/blender/editors/physics/rigidbody_object.c
@@ -520,6 +520,26 @@ static int rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+static bool mass_calculate_poll_property(const bContext *UNUSED(C),
+ wmOperator *op,
+ const PropertyRNA *prop)
+{
+ const char *prop_id = RNA_property_identifier(prop);
+
+ /* Disable density input when not using the 'Custom' preset. */
+ if (STREQ(prop_id, "density")) {
+ int material = RNA_enum_get(op->ptr, "material");
+ if (material >= 0) {
+ RNA_def_property_clear_flag((PropertyRNA *)prop, PROP_EDITABLE);
+ }
+ else {
+ RNA_def_property_flag((PropertyRNA *)prop, PROP_EDITABLE);
+ }
+ }
+
+ return true;
+}
+
void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -533,6 +553,7 @@ void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
ot->invoke = WM_menu_invoke; /* XXX */
ot->exec = rigidbody_objects_calc_mass_exec;
ot->poll = ED_operator_rigidbody_active_poll;
+ ot->poll_property = mass_calculate_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -554,7 +575,7 @@ void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
FLT_MIN,
FLT_MAX,
"Density",
- "Custom density value (kg/m^3) to use instead of material preset",
+ "Density value (kg/m^3), allows custom value if the 'Custom' preset is used",
1.0f,
2500.0f);
}
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 0b5a8db0115..0bec509cd7e 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -574,9 +574,12 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
return;
}
if (rj->image_outdated) {
- /* update entire render */
+ /* Free all render buffer caches when switching slots, with lock to ensure main
+ * thread is not drawing the buffer at the same time. */
rj->image_outdated = false;
- BKE_image_signal(rj->main, ima, NULL, IMA_SIGNAL_COLORMANAGE);
+ ibuf = BKE_image_acquire_ibuf(ima, &rj->iuser, &lock);
+ BKE_image_free_buffers(ima);
+ BKE_image_release_ibuf(ima, ibuf, lock);
*(rj->do_update) = true;
return;
}
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 48f937fb4ec..cfc07de3f6c 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -859,7 +859,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
oglrender->task_pool = BLI_task_pool_create_background_serial(oglrender, TASK_PRIORITY_LOW);
}
else {
- oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW);
+ oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
}
oglrender->pool_ok = true;
BLI_spin_init(&oglrender->reports_lock);
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 1b7209b164b..0b0009c2a2f 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -70,6 +70,7 @@
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "BKE_texture.h"
#include "BKE_world.h"
@@ -610,9 +611,7 @@ static bool ed_preview_draw_rect(ScrArea *area, int split, int first, rcti *rect
float fy = rect->ymin;
/* material preview only needs monoscopy (view 0) */
- if (re) {
- RE_AcquiredResultGet32(re, &rres, (uint *)rect_byte, 0);
- }
+ RE_AcquiredResultGet32(re, &rres, (uint *)rect_byte, 0);
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
immDrawPixelsTex(
@@ -692,8 +691,9 @@ struct ObjectPreviewData {
int sizey;
};
-static Object *object_preview_camera_create(
- Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey)
+static Object *object_preview_camera_create(Main *preview_main,
+ ViewLayer *view_layer,
+ Object *preview_object)
{
Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera");
@@ -701,18 +701,17 @@ static Object *object_preview_camera_create(
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. */
+ /* Camera is Y up, so needs additional rotations to obliquely face the front. */
float drotmat[3][3];
- axis_angle_to_mat3_single(drotmat, 'X', M_PI_2);
+ const float eul[3] = {M_PI * 0.4f, 0.0f, M_PI * 0.1f};
+ eul_to_mat3(drotmat, eul);
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;
- }
+ /* Nice focal length for close portraiture. */
+ ((Camera *)camera->data)->lens = 85;
return camera;
}
@@ -730,11 +729,8 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe
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);
+ Object *camera_object = object_preview_camera_create(
+ preview_data->pr_main, view_layer, preview_data->object);
scene->camera = camera_object;
scene->r.xsch = preview_data->sizex;
@@ -779,16 +775,21 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
U.pixelsize = 2.0f;
+ View3DShading shading;
+ BKE_screen_view3d_shading_init(&shading);
+ /* Enable shadows, makes it a bit easier to see the shape. */
+ shading.flag |= V3D_SHADING_SHADOW;
+
ImBuf *ibuf = ED_view3d_draw_offscreen_imbuf_simple(
depsgraph,
DEG_get_evaluated_scene(depsgraph),
- NULL,
- OB_SOLID,
+ &shading,
+ OB_TEXTURE,
DEG_get_evaluated_object(depsgraph, scene->camera),
preview_sized->sizex,
preview_sized->sizey,
IB_rect,
- V3D_OFSDRAW_NONE,
+ V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS,
R_ALPHAPREMUL,
NULL,
NULL,
@@ -1608,7 +1609,7 @@ static void icon_preview_free(void *customdata)
}
void ED_preview_icon_render(
- Main *bmain, Depsgraph *depsgraph, Scene *scene, ID *id, uint *rect, int sizex, int sizey)
+ const bContext *C, Scene *scene, ID *id, uint *rect, int sizex, int sizey)
{
IconPreview ip = {NULL};
short stop = false, update = false;
@@ -1616,9 +1617,9 @@ void ED_preview_icon_render(
ED_preview_ensure_dbase();
- ip.bmain = bmain;
+ ip.bmain = CTX_data_main(C);
ip.scene = scene;
- ip.depsgraph = depsgraph;
+ ip.depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ip.owner = BKE_previewimg_id_ensure(id);
ip.id = id;
/* Control isn't given back to the caller until the preview is done. So we don't need to copy
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 4ed1cbd60a5..3e8a1bda2f0 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -188,7 +188,7 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data)
ED_render_engine_area_exit(bmain, area);
}
}
- RE_FreePersistentData();
+ RE_FreePersistentData(NULL);
/* Inform all render engines and draw managers. */
DEGEditorUpdateContext update_ctx = {NULL};
update_ctx.bmain = bmain;
diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c
index 465438f814a..eb4a040e891 100644
--- a/source/blender/editors/render/render_view.c
+++ b/source/blender/editors/render/render_view.c
@@ -164,6 +164,7 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports)
sizex,
sizey,
SPACE_IMAGE,
+ true,
false,
true,
WIN_ALIGN_LOCATION_CENTER) == NULL) {
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index bd2b1c4c553..2814a4c9351 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1172,12 +1172,12 @@ static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *reg
}
/* dir is direction to check, not the splitting edge direction! */
-static int rct_fits(const rcti *rect, char dir, int size)
+static int rct_fits(const rcti *rect, const eScreenAxis dir_axis, int size)
{
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
return BLI_rcti_size_x(rect) + 1 - size;
}
- /* 'v' */
+ /* Vertical. */
return BLI_rcti_size_y(rect) + 1 - size;
}
@@ -1398,7 +1398,8 @@ static void region_rect_recursive(
region->flag |= RGN_FLAG_TOO_SMALL;
}
}
- else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) {
+ else if (rct_fits(remainder, SCREEN_AXIS_V, 1) < 0 ||
+ rct_fits(remainder, SCREEN_AXIS_H, 1) < 0) {
/* remainder is too small for any usage */
region->flag |= RGN_FLAG_TOO_SMALL;
}
@@ -1410,11 +1411,11 @@ static void region_rect_recursive(
else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
- if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) {
+ if ((prefsizey == 0) || (rct_fits(winrct, SCREEN_AXIS_V, prefsizey) < 0)) {
region->flag |= RGN_FLAG_TOO_SMALL;
}
else {
- int fac = rct_fits(winrct, 'v', prefsizey);
+ int fac = rct_fits(winrct, SCREEN_AXIS_V, prefsizey);
if (fac < 0) {
prefsizey += fac;
@@ -1436,11 +1437,11 @@ static void region_rect_recursive(
else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
- if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) {
+ if ((prefsizex == 0) || (rct_fits(winrct, SCREEN_AXIS_H, prefsizex) < 0)) {
region->flag |= RGN_FLAG_TOO_SMALL;
}
else {
- int fac = rct_fits(winrct, 'h', prefsizex);
+ int fac = rct_fits(winrct, SCREEN_AXIS_H, prefsizex);
if (fac < 0) {
prefsizex += fac;
@@ -1464,7 +1465,7 @@ static void region_rect_recursive(
region->winrct = *remainder;
if (alignment == RGN_ALIGN_HSPLIT) {
- if (rct_fits(remainder, 'h', prefsizex) > 4) {
+ if (rct_fits(remainder, SCREEN_AXIS_H, prefsizex) > 4) {
region->winrct.xmax = BLI_rcti_cent_x(remainder);
remainder->xmin = region->winrct.xmax + 1;
}
@@ -1473,7 +1474,7 @@ static void region_rect_recursive(
}
}
else {
- if (rct_fits(remainder, 'v', prefsizey) > 4) {
+ if (rct_fits(remainder, SCREEN_AXIS_V, prefsizey) > 4) {
region->winrct.ymax = BLI_rcti_cent_y(remainder);
remainder->ymin = region->winrct.ymax + 1;
}
@@ -1526,8 +1527,8 @@ static void region_rect_recursive(
BLI_rcti_init(remainder, 0, 0, 0, 0);
}
- /* Fix any negative dimensions. This can happen when a quad split 3d view gets to small. (see
- * T72200). */
+ /* Fix any negative dimensions. This can happen when a quad split 3d view gets too small.
+ * (see T72200). */
BLI_rcti_sanitize(&region->winrct);
quad++;
diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c
index 2ba7ef8f972..2c45524ef94 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -33,184 +33,11 @@
#include "WM_api.h"
+#include "UI_interface.h"
#include "UI_resources.h"
#include "screen_intern.h"
-/**
- * Draw horizontal shape visualizing future joining
- * (left as well right direction of future joining).
- */
-static void draw_horizontal_join_shape(ScrArea *area, char dir, uint pos)
-{
- const float width = screen_geom_area_width(area) - 1;
- const float height = screen_geom_area_height(area) - 1;
-
- float w, h;
- if (height < width) {
- h = height / 8;
- w = height / 4;
- }
- else {
- h = width / 8;
- w = width / 4;
- }
-
- vec2f points[10];
- points[0].x = area->v1->vec.x;
- points[0].y = area->v1->vec.y + height / 2;
-
- points[1].x = area->v1->vec.x;
- points[1].y = area->v1->vec.y;
-
- points[2].x = area->v4->vec.x - w;
- points[2].y = area->v4->vec.y;
-
- points[3].x = area->v4->vec.x - w;
- points[3].y = area->v4->vec.y + height / 2 - 2 * h;
-
- points[4].x = area->v4->vec.x - 2 * w;
- points[4].y = area->v4->vec.y + height / 2;
-
- points[5].x = area->v4->vec.x - w;
- points[5].y = area->v4->vec.y + height / 2 + 2 * h;
-
- points[6].x = area->v3->vec.x - w;
- points[6].y = area->v3->vec.y;
-
- points[7].x = area->v2->vec.x;
- points[7].y = area->v2->vec.y;
-
- points[8].x = area->v4->vec.x;
- points[8].y = area->v4->vec.y + height / 2 - h;
-
- points[9].x = area->v4->vec.x;
- points[9].y = area->v4->vec.y + height / 2 + h;
-
- if (dir == 'l') {
- /* when direction is left, then we flip direction of arrow */
- float cx = area->v1->vec.x + width;
- for (int i = 0; i < 10; i++) {
- points[i].x -= cx;
- points[i].x = -points[i].x;
- points[i].x += area->v1->vec.x;
- }
- }
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 0; i < 5; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immEnd();
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 4; i < 8; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immVertex2f(pos, points[0].x, points[0].y);
- immEnd();
-
- immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y);
- immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y);
-}
-
-/**
- * Draw vertical shape visualizing future joining (up/down direction).
- */
-static void draw_vertical_join_shape(ScrArea *area, char dir, uint pos)
-{
- const float width = screen_geom_area_width(area) - 1;
- const float height = screen_geom_area_height(area) - 1;
-
- float w, h;
- if (height < width) {
- h = height / 4;
- w = height / 8;
- }
- else {
- h = width / 4;
- w = width / 8;
- }
-
- vec2f points[10];
- points[0].x = area->v1->vec.x + width / 2;
- points[0].y = area->v3->vec.y;
-
- points[1].x = area->v2->vec.x;
- points[1].y = area->v2->vec.y;
-
- points[2].x = area->v1->vec.x;
- points[2].y = area->v1->vec.y + h;
-
- points[3].x = area->v1->vec.x + width / 2 - 2 * w;
- points[3].y = area->v1->vec.y + h;
-
- points[4].x = area->v1->vec.x + width / 2;
- points[4].y = area->v1->vec.y + 2 * h;
-
- points[5].x = area->v1->vec.x + width / 2 + 2 * w;
- points[5].y = area->v1->vec.y + h;
-
- points[6].x = area->v4->vec.x;
- points[6].y = area->v4->vec.y + h;
-
- points[7].x = area->v3->vec.x;
- points[7].y = area->v3->vec.y;
-
- points[8].x = area->v1->vec.x + width / 2 - w;
- points[8].y = area->v1->vec.y;
-
- points[9].x = area->v1->vec.x + width / 2 + w;
- points[9].y = area->v1->vec.y;
-
- if (dir == 'u') {
- /* when direction is up, then we flip direction of arrow */
- float cy = area->v1->vec.y + height;
- for (int i = 0; i < 10; i++) {
- points[i].y -= cy;
- points[i].y = -points[i].y;
- points[i].y += area->v1->vec.y;
- }
- }
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 0; i < 5; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immEnd();
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 4; i < 8; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immVertex2f(pos, points[0].x, points[0].y);
- immEnd();
-
- immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y);
- immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y);
-}
-
-/**
- * Draw join shape due to direction of joining.
- */
-static void draw_join_shape(ScrArea *area, char dir, uint pos)
-{
- if (ELEM(dir, 'u', 'd')) {
- draw_vertical_join_shape(area, dir, pos);
- }
- else {
- draw_horizontal_join_shape(area, dir, pos);
- }
-}
-
#define CORNER_RESOLUTION 3
static void do_vert_pair(GPUVertBuf *vbo, uint pos, uint *vidx, int corner, int i)
@@ -290,28 +117,6 @@ static GPUBatch *batch_screen_edges_get(int *corner_len)
#undef CORNER_RESOLUTION
-/**
- * Draw screen area darker with arrow (visualization of future joining).
- */
-static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos)
-{
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ub(0, 0, 0, 50);
-
- draw_join_shape(area, dir, pos);
-}
-
-/**
- * Draw screen area lighter with arrow shape ("eraser" of previous dark shape).
- */
-static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos)
-{
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ub(255, 255, 255, 25);
-
- immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y);
-}
-
static void drawscredge_area_draw(
int sizex, int sizey, short x1, short y1, short x2, short y2, float edge_thickness)
{
@@ -427,53 +232,97 @@ void ED_screen_draw_edges(wmWindow *win)
}
/**
- * The blended join arrows.
+ * Visual indication of the two areas involved in a proposed join.
*
* \param sa1: Area from which the resultant originates.
* \param sa2: Target area that will be replaced.
*/
-void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2)
+void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return;
+ }
+
+ /* Rect of the combined areas.*/
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ const rctf combined = {
+ .xmin = vertical ? MAX2(sa1->totrct.xmin, sa2->totrct.xmin) :
+ MIN2(sa1->totrct.xmin, sa2->totrct.xmin),
+ .xmax = vertical ? MIN2(sa1->totrct.xmax, sa2->totrct.xmax) :
+ MAX2(sa1->totrct.xmax, sa2->totrct.xmax),
+ .ymin = vertical ? MIN2(sa1->totrct.ymin, sa2->totrct.ymin) :
+ MAX2(sa1->totrct.ymin, sa2->totrct.ymin),
+ .ymax = vertical ? MAX2(sa1->totrct.ymax, sa2->totrct.ymax) :
+ MIN2(sa1->totrct.ymax, sa2->totrct.ymax),
+ };
+
+ uint pos_id = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_blend(GPU_BLEND_ALPHA);
- GPU_line_width(1);
-
- /* blended join arrow */
- int dir = area_getorientation(sa1, sa2);
- int dira = -1;
- if (dir != -1) {
- switch (dir) {
- case 0: /* W */
- dir = 'r';
- dira = 'l';
- break;
- case 1: /* N */
- dir = 'd';
- dira = 'u';
- break;
- case 2: /* E */
- dir = 'l';
- dira = 'r';
- break;
- case 3: /* S */
- dir = 'u';
- dira = 'd';
- break;
+ /* Highlight source (sa1) within combined area. */
+ immUniformColor4fv((const float[4]){1.0f, 1.0f, 1.0f, 0.10f});
+ immRectf(pos_id,
+ MAX2(sa1->totrct.xmin, combined.xmin),
+ MAX2(sa1->totrct.ymin, combined.ymin),
+ MIN2(sa1->totrct.xmax, combined.xmax),
+ MIN2(sa1->totrct.ymax, combined.ymax));
+
+ /* Highlight destination (sa2) within combined area. */
+ immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.25f});
+ immRectf(pos_id,
+ MAX2(sa2->totrct.xmin, combined.xmin),
+ MAX2(sa2->totrct.ymin, combined.ymin),
+ MIN2(sa2->totrct.xmax, combined.xmax),
+ MIN2(sa2->totrct.ymax, combined.ymax));
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+ if (offset1 < 0 || offset2 > 0) {
+ /* Show partial areas that will be closed. */
+ immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.8f});
+ if (vertical) {
+ if (sa1->totrct.xmin < combined.xmin) {
+ immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax);
+ }
+ if (sa2->totrct.xmin < combined.xmin) {
+ immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax);
+ }
+ if (sa1->totrct.xmax > combined.xmax) {
+ immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax);
+ }
+ if (sa2->totrct.xmax > combined.xmax) {
+ immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax);
+ }
+ }
+ else {
+ if (sa1->totrct.ymin < combined.ymin) {
+ immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin);
+ }
+ if (sa2->totrct.ymin < combined.ymin) {
+ immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin);
+ }
+ if (sa1->totrct.ymax > combined.ymax) {
+ immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax);
+ }
+ if (sa2->totrct.ymax > combined.ymax) {
+ immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax);
+ }
}
-
- GPU_blend(GPU_BLEND_ALPHA);
-
- scrarea_draw_shape_dark(sa2, dir, pos);
- scrarea_draw_shape_light(sa1, dira, pos);
-
- GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
+
+ /* Outline the combined area. */
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, (float[4]){1.0f, 1.0f, 1.0f, 0.8f});
}
-void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
+void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const float fac)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -485,7 +334,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
immBegin(GPU_PRIM_LINES, 2);
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
const float y = (1 - fac) * area->totrct.ymin + fac * area->totrct.ymax;
immVertex2f(pos, area->totrct.xmin, y);
@@ -503,7 +352,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
immEnd();
}
else {
- BLI_assert(dir == 'v');
+ BLI_assert(dir_axis == SCREEN_AXIS_V);
const float x = (1 - fac) * area->totrct.xmin + fac * area->totrct.xmax;
immVertex2f(pos, x, area->totrct.ymin);
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 7ad8eada3b9..6cb184a3394 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -104,8 +104,12 @@ static void screen_delarea(bContext *C, bScreen *screen, ScrArea *area)
MEM_freeN(area);
}
-ScrArea *area_split(
- const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge)
+ScrArea *area_split(const wmWindow *win,
+ bScreen *screen,
+ ScrArea *area,
+ const eScreenAxis dir_axis,
+ const float fac,
+ const bool merge)
{
ScrArea *newa = NULL;
@@ -116,7 +120,7 @@ ScrArea *area_split(
rcti window_rect;
WM_window_rect_calc(win, &window_rect);
- short split = screen_geom_find_area_split_point(area, &window_rect, dir, fac);
+ short split = screen_geom_find_area_split_point(area, &window_rect, dir_axis, fac);
if (split == 0) {
return NULL;
}
@@ -125,7 +129,7 @@ ScrArea *area_split(
* normally it shouldn't matter which is used since the copy should match the original
* however with viewport rendering and python console this isn't the case. - campbell */
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
/* new vertices */
ScrVert *sv1 = screen_geom_vertex_add(screen, area->v1->vec.x, split);
ScrVert *sv2 = screen_geom_vertex_add(screen, area->v4->vec.x, split);
@@ -279,47 +283,44 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new)
screen_new->do_draw = true;
}
-/* with area as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
-/* -1 = not valid check */
-/* used with join operator */
-int area_getorientation(ScrArea *area, ScrArea *sb)
+/**
+ * with `sa_a` as center, `sa_b` is located at: 0=W, 1=N, 2=E, 3=S
+ * -1 = not valid check.
+ * used with join operator.
+ */
+eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b)
{
- if (area == NULL || sb == NULL) {
- return -1;
+ if (sa_a == NULL || sa_b == NULL || sa_a == sa_b) {
+ return SCREEN_DIR_NONE;
}
- ScrVert *saBL = area->v1;
- ScrVert *saTL = area->v2;
- ScrVert *saTR = area->v3;
- ScrVert *saBR = area->v4;
+ const vec2s *sa_bl = &sa_a->v1->vec;
+ const vec2s *sa_tl = &sa_a->v2->vec;
+ const vec2s *sa_tr = &sa_a->v3->vec;
+ const vec2s *sa_br = &sa_a->v4->vec;
- ScrVert *sbBL = sb->v1;
- ScrVert *sbTL = sb->v2;
- ScrVert *sbTR = sb->v3;
- ScrVert *sbBR = sb->v4;
+ const vec2s *sb_bl = &sa_b->v1->vec;
+ const vec2s *sb_tl = &sa_b->v2->vec;
+ const vec2s *sb_tr = &sa_b->v3->vec;
+ const vec2s *sb_br = &sa_b->v4->vec;
- if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */
- if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) &&
- (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) {
+ if (sa_bl->x == sb_br->x && sa_tl->x == sb_tr->x) { /* sa_a to right of sa_b = W */
+ if ((MIN2(sa_tl->y, sb_tr->y) - MAX2(sa_bl->y, sb_br->y)) > AREAJOINTOLERANCEY) {
return 0;
}
}
- else if (saTL->vec.y == sbBL->vec.y &&
- saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */
- if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) &&
- (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) {
+ else if (sa_tl->y == sb_bl->y && sa_tr->y == sb_br->y) { /* sa_a to bottom of sa_b = N */
+ if ((MIN2(sa_tr->x, sb_br->x) - MAX2(sa_tl->x, sb_bl->x)) > AREAJOINTOLERANCEX) {
return 1;
}
}
- else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */
- if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) &&
- (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) {
+ else if (sa_tr->x == sb_tl->x && sa_br->x == sb_bl->x) { /* sa_a to left of sa_b = E */
+ if ((MIN2(sa_tr->y, sb_tl->y) - MAX2(sa_br->y, sb_bl->y)) > AREAJOINTOLERANCEY) {
return 2;
}
}
- else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/
- if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) &&
- (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) {
+ else if (sa_bl->y == sb_tl->y && sa_br->y == sb_tr->y) { /* sa_a on top of sa_b = S */
+ if ((MIN2(sa_br->x, sb_tr->x) - MAX2(sa_bl->x, sb_tl->x)) > AREAJOINTOLERANCEX) {
return 3;
}
}
@@ -327,6 +328,39 @@ int area_getorientation(ScrArea *area, ScrArea *sb)
return -1;
}
+/**
+ * Get alignment offset of adjacent areas. 'dir' value is like #area_getorientation().
+ */
+void area_getoffsets(
+ ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2)
+{
+ if (sa_a == NULL || sa_b == NULL) {
+ *r_offset1 = INT_MAX;
+ *r_offset2 = INT_MAX;
+ }
+ else if (dir == SCREEN_DIR_W) { /* West: sa on right and sa_b to the left. */
+ *r_offset1 = sa_b->v3->vec.y - sa_a->v2->vec.y;
+ *r_offset2 = sa_b->v4->vec.y - sa_a->v1->vec.y;
+ }
+ else if (dir == SCREEN_DIR_N) { /* North: sa below and sa_b above. */
+ *r_offset1 = sa_a->v2->vec.x - sa_b->v1->vec.x;
+ *r_offset2 = sa_a->v3->vec.x - sa_b->v4->vec.x;
+ }
+ else if (dir == SCREEN_DIR_E) { /* East: sa on left and sa_b to the right. */
+ *r_offset1 = sa_b->v2->vec.y - sa_a->v3->vec.y;
+ *r_offset2 = sa_b->v1->vec.y - sa_a->v4->vec.y;
+ }
+ else if (dir == SCREEN_DIR_S) { /* South: sa above and sa_b below. */
+ *r_offset1 = sa_a->v1->vec.x - sa_b->v2->vec.x;
+ *r_offset2 = sa_a->v4->vec.x - sa_b->v3->vec.x;
+ }
+ else {
+ BLI_assert(dir == SCREEN_DIR_NONE);
+ *r_offset1 = INT_MAX;
+ *r_offset2 = INT_MAX;
+ }
+}
+
/* Screen verts with horizontal position equal to from_x are moved to to_x. */
static void screen_verts_halign(const wmWindow *win,
const bScreen *screen,
@@ -358,11 +392,11 @@ static void screen_verts_valign(const wmWindow *win,
/* Adjust all screen edges to allow joining two areas. 'dir' value is like area_getorientation().
*/
static void screen_areas_align(
- bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const int dir)
+ bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const eScreenDir dir)
{
wmWindow *win = CTX_wm_window(C);
- if (ELEM(dir, 0, 2)) {
+ if (SCREEN_DIR_IS_HORIZONTAL(dir)) {
/* horizontal join, use average for new top and bottom. */
int top = (sa1->v2->vec.y + sa2->v2->vec.y) / 2;
int bottom = (sa1->v4->vec.y + sa2->v4->vec.y) / 2;
@@ -390,41 +424,47 @@ static void screen_areas_align(
}
}
-/* Helper function to join 2 areas, it has a return value, 0=failed 1=success
- * used by the split, join operators
- */
-int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
+/* Simple join of two areas without any splitting. Will return false if not possible. */
+static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
{
- int dir = area_getorientation(sa1, sa2);
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return false;
+ }
- if (dir == -1) {
- return 0;
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+
+ int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX;
+ if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) {
+ return false;
}
- /* Align areas if they are not. Do sanity checking before getting here. */
+ /* Align areas if they are not. */
screen_areas_align(C, screen, sa1, sa2, dir);
- if (dir == 0) { /* sa1 to right of sa2 = W */
- sa1->v1 = sa2->v1; /* BL */
- sa1->v2 = sa2->v2; /* TL */
+ if (dir == SCREEN_DIR_W) { /* sa1 to right of sa2 = West. */
+ sa1->v1 = sa2->v1; /* BL */
+ sa1->v2 = sa2->v2; /* TL */
screen_geom_edge_add(screen, sa1->v2, sa1->v3);
screen_geom_edge_add(screen, sa1->v1, sa1->v4);
}
- else if (dir == 1) { /* sa1 to bottom of sa2 = N */
- sa1->v2 = sa2->v2; /* TL */
- sa1->v3 = sa2->v3; /* TR */
+ else if (dir == SCREEN_DIR_N) { /* sa1 to bottom of sa2 = North. */
+ sa1->v2 = sa2->v2; /* TL */
+ sa1->v3 = sa2->v3; /* TR */
screen_geom_edge_add(screen, sa1->v1, sa1->v2);
screen_geom_edge_add(screen, sa1->v3, sa1->v4);
}
- else if (dir == 2) { /* sa1 to left of sa2 = E */
- sa1->v3 = sa2->v3; /* TR */
- sa1->v4 = sa2->v4; /* BR */
+ else if (dir == SCREEN_DIR_E) { /* sa1 to left of sa2 = East. */
+ sa1->v3 = sa2->v3; /* TR */
+ sa1->v4 = sa2->v4; /* BR */
screen_geom_edge_add(screen, sa1->v2, sa1->v3);
screen_geom_edge_add(screen, sa1->v1, sa1->v4);
}
- else if (dir == 3) { /* sa1 on top of sa2 = S */
- sa1->v1 = sa2->v1; /* BL */
- sa1->v4 = sa2->v4; /* BR */
+ else if (dir == SCREEN_DIR_S) { /* sa1 on top of sa2 = South. */
+ sa1->v1 = sa2->v1; /* BL */
+ sa1->v4 = sa2->v4; /* BR */
screen_geom_edge_add(screen, sa1->v1, sa1->v2);
screen_geom_edge_add(screen, sa1->v3, sa1->v4);
}
@@ -434,7 +474,104 @@ int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
/* Update preview thumbnail */
BKE_icon_changed(screen->id.icon_id);
- return 1;
+ return true;
+}
+
+/* Slice off and return new area. "Reverse" gives right/bottom, rather than left/top. */
+static ScrArea *screen_area_trim(
+ bContext *C, bScreen *screen, ScrArea **area, int size, eScreenDir dir, bool reverse)
+{
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ if (abs(size) < (vertical ? AREAJOINTOLERANCEX : AREAJOINTOLERANCEY)) {
+ return NULL;
+ }
+
+ /* Measurement with ScrVerts because winx and winy might not be correct at this time. */
+ float fac = abs(size) / (float)(vertical ? ((*area)->v3->vec.x - (*area)->v1->vec.x) :
+ ((*area)->v3->vec.y - (*area)->v1->vec.y));
+ fac = (reverse == vertical) ? 1.0f - fac : fac;
+ ScrArea *newsa = area_split(
+ CTX_wm_window(C), screen, *area, vertical ? SCREEN_AXIS_V : SCREEN_AXIS_H, fac, true);
+
+ /* area_split always returns smallest of the two areas, so might have to swap. */
+ if (((fac > 0.5f) == vertical) != reverse) {
+ ScrArea *temp = *area;
+ *area = newsa;
+ newsa = temp;
+ }
+
+ return newsa;
+}
+
+/* Join any two neighboring areas. Might create new areas, kept if over min_remainder. */
+static bool screen_area_join_ex(
+ bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, bool close_all_remainders)
+{
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return false;
+ }
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+
+ /* Split Left/Top into new area if overhanging. */
+ ScrArea *side1 = screen_area_trim(C, screen, (offset1 > 0) ? &sa2 : &sa1, offset1, dir, false);
+
+ /* Split Right/Bottom into new area if overhanging. */
+ ScrArea *side2 = screen_area_trim(C, screen, (offset2 > 0) ? &sa1 : &sa2, offset2, dir, true);
+
+ /* The two areas now line up, so join them. */
+ screen_area_join_aligned(C, screen, sa1, sa2);
+
+ if (close_all_remainders || offset1 < 0 || offset2 > 0) {
+ /* Close both if trimming `sa1`. */
+ screen_area_close(C, screen, side1);
+ screen_area_close(C, screen, side2);
+ }
+
+ BKE_icon_changed(screen->id.icon_id);
+ return true;
+}
+
+/* Join any two neighboring areas. Might involve complex changes. */
+int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
+{
+ return screen_area_join_ex(C, screen, sa1, sa2, false);
+}
+
+/* Close a screen area, allowing most-aligned neighbor to take its place. */
+bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area)
+{
+ if (area == NULL) {
+ return false;
+ }
+
+ ScrArea *sa2 = NULL;
+ float best_alignment = 0.0f;
+
+ LISTBASE_FOREACH (ScrArea *, neighbor, &screen->areabase) {
+ const eScreenDir dir = area_getorientation(area, neighbor);
+ /* Must at least partially share an edge and not be a global area. */
+ if ((dir != SCREEN_DIR_NONE) && (neighbor->global == NULL)) {
+ /* Winx/Winy might not be updated yet, so get lengths from verts. */
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ const int area_length = vertical ? (area->v3->vec.x - area->v1->vec.x) :
+ (area->v3->vec.y - area->v1->vec.y);
+ const int ar_length = vertical ? (neighbor->v3->vec.x - neighbor->v1->vec.x) :
+ (neighbor->v3->vec.y - neighbor->v1->vec.y);
+ /* Calculate the ratio of the lengths of the shared edges. */
+ float alignment = MIN2(area_length, ar_length) / (float)MAX2(area_length, ar_length);
+ if (alignment > best_alignment) {
+ best_alignment = alignment;
+ sa2 = neighbor;
+ }
+ }
+ }
+
+ /* Join from neighbor into this area to close it. */
+ return screen_area_join_ex(C, screen, sa2, area, true);
}
/* ****************** EXPORTED API TO OTHER MODULES *************************** */
@@ -1448,6 +1585,7 @@ ScrArea *ED_screen_temp_space_open(bContext *C,
sizex,
sizey,
(int)space_type,
+ false,
dialog,
true,
WIN_ALIGN_LOCATION_CENTER)) {
diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c
index ac159f4d633..51edad0332b 100644
--- a/source/blender/editors/screen/screen_geometry.c
+++ b/source/blender/editors/screen/screen_geometry.c
@@ -304,7 +304,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
*/
short screen_geom_find_area_split_point(const ScrArea *area,
const rcti *window_rect,
- char dir,
+ const eScreenAxis dir_axis,
float fac)
{
const int cur_area_width = screen_geom_area_width(area);
@@ -313,17 +313,21 @@ short screen_geom_find_area_split_point(const ScrArea *area,
const short area_min_y = ED_area_headersize();
/* area big enough? */
- if ((dir == 'v') && (cur_area_width <= 2 * area_min_x)) {
- return 0;
+ if (dir_axis == SCREEN_AXIS_V) {
+ if (cur_area_width <= 2 * area_min_x) {
+ return 0;
+ }
}
- if ((dir == 'h') && (cur_area_height <= 2 * area_min_y)) {
- return 0;
+ else if (dir_axis == SCREEN_AXIS_H) {
+ if (cur_area_height <= 2 * area_min_y) {
+ return 0;
+ }
}
/* to be sure */
CLAMP(fac, 0.0f, 1.0f);
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
short y = area->v1->vec.y + round_fl_to_short(fac * cur_area_height);
int area_min = area_min_y;
@@ -373,13 +377,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
{
bScreen *screen = WM_window_get_active_screen(win);
- /* 'dir' is the direction of EDGE */
- char dir;
+ /* 'dir_axis' is the direction of EDGE */
+ eScreenAxis dir_axis;
if (edge->v1->vec.x == edge->v2->vec.x) {
- dir = 'v';
+ dir_axis = SCREEN_AXIS_V;
}
else {
- dir = 'h';
+ dir_axis = SCREEN_AXIS_H;
}
ED_screen_verts_iter(win, screen, sv)
@@ -396,13 +400,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
oneselected = false;
LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) {
if (se->v1->flag + se->v2->flag == 1) {
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
if (se->v1->vec.y == se->v2->vec.y) {
se->v1->flag = se->v2->flag = 1;
oneselected = true;
}
}
- if (dir == 'v') {
+ else if (dir_axis == SCREEN_AXIS_V) {
if (se->v1->vec.x == se->v2->vec.x) {
se->v1->flag = se->v2->flag = 1;
oneselected = true;
diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h
index c51ff559786..683f2844371 100644
--- a/source/blender/editors/screen/screen_intern.h
+++ b/source/blender/editors/screen/screen_intern.h
@@ -29,12 +29,37 @@ struct bContextDataResult;
/* internal exports only */
+typedef enum eScreenDir {
+ /** This can mean unset, unknown or invalid. */
+ SCREEN_DIR_NONE = -1,
+ /** West/Left. */
+ SCREEN_DIR_W = 0,
+ /** North/Up. */
+ SCREEN_DIR_N = 1,
+ /** East/Right. */
+ SCREEN_DIR_E = 2,
+ /** South/Down. */
+ SCREEN_DIR_S = 3,
+} eScreenDir;
+
+#define SCREEN_DIR_IS_VERTICAL(dir) (ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S))
+#define SCREEN_DIR_IS_HORIZONTAL(dir) (ELEM(dir, SCREEN_DIR_W, SCREEN_DIR_E))
+
+typedef enum eScreenAxis {
+ /** Horizontal. */
+ SCREEN_AXIS_H = 'h',
+ /** Vertical. */
+ SCREEN_AXIS_V = 'v',
+} eScreenAxis;
+
#define AZONESPOTW UI_HEADER_OFFSET /* width of corner #AZone - max */
#define AZONESPOTH (0.6f * U.widget_unit) /* height of corner #AZone */
#define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */
#define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */
-#define AREAJOINTOLERANCE (1.0f * U.widget_unit) /* Edges must be close to allow joining. */
+/* Edges must be within these to allow joining. */
+#define AREAJOINTOLERANCEX (AREAMINX * U.dpi_fac)
+#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac)
/* Expanded interaction influence of area borders. */
#define BORDERPADDING (U.dpi_fac + U.pixelsize)
@@ -44,6 +69,10 @@ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src);
void region_toggle_hidden(struct bContext *C, ARegion *region, const bool do_fade);
+/* screen_draw.c */
+void screen_draw_join_highlight(struct ScrArea *sa1, struct ScrArea *sa2);
+void screen_draw_split_preview(struct ScrArea *area, const eScreenAxis dir_axis, const float fac);
+
/* screen_edit.c */
bScreen *screen_add(struct Main *bmain, const char *name, const rcti *rect);
void screen_data_copy(bScreen *to, bScreen *from);
@@ -54,11 +83,17 @@ void screen_change_prepare(bScreen *screen_old,
struct Main *bmain,
struct bContext *C,
wmWindow *win);
-ScrArea *area_split(
- const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge);
+ScrArea *area_split(const wmWindow *win,
+ bScreen *screen,
+ ScrArea *area,
+ const eScreenAxis dir_axis,
+ const float fac,
+ const bool merge);
int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2);
-int area_getorientation(ScrArea *area, ScrArea *sb);
-
+eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b);
+void area_getoffsets(
+ ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2);
+bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area);
struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]);
/* screen_geometry.c */
@@ -80,7 +115,7 @@ ScrEdge *screen_geom_find_active_scredge(const wmWindow *win,
void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen);
short screen_geom_find_area_split_point(const ScrArea *area,
const rcti *window_rect,
- char dir,
+ const eScreenAxis dir_axis,
float fac);
void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge);
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 797c3bcf132..373bde99e24 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -141,6 +141,14 @@ bool ED_operator_screenactive(bContext *C)
return true;
}
+bool ED_operator_screenactive_nobackground(bContext *C)
+{
+ if (G.background) {
+ return false;
+ }
+ return ED_operator_screenactive(C);
+}
+
/* XXX added this to prevent anim state to change during renders */
static bool ED_operator_screenactive_norender(bContext *C)
{
@@ -686,7 +694,9 @@ static bool screen_active_editable(bContext *C)
typedef struct sActionzoneData {
ScrArea *sa1, *sa2;
AZone *az;
- int x, y, gesture_dir, modifier;
+ int x, y;
+ eScreenDir gesture_dir;
+ int modifier;
} sActionzoneData;
/* quick poll to save operators to be created and handled */
@@ -1037,16 +1047,16 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Calculate gesture cardinal direction. */
if (delta_y > abs(delta_x)) {
- sad->gesture_dir = 'n';
+ sad->gesture_dir = SCREEN_DIR_N;
}
else if (delta_x >= abs(delta_y)) {
- sad->gesture_dir = 'e';
+ sad->gesture_dir = SCREEN_DIR_E;
}
else if (delta_y < -abs(delta_x)) {
- sad->gesture_dir = 's';
+ sad->gesture_dir = SCREEN_DIR_S;
}
else {
- sad->gesture_dir = 'w';
+ sad->gesture_dir = SCREEN_DIR_W;
}
bool is_gesture;
@@ -1063,22 +1073,24 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Are we still in same area? */
if (BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y) == sad->sa1) {
/* Same area, so possible split. */
- WM_cursor_set(
- win, (ELEM(sad->gesture_dir, 'n', 's')) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
+ WM_cursor_set(win,
+ SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT :
+ WM_CURSOR_V_SPLIT);
is_gesture = (delta_max > split_threshold);
}
else {
/* Different area, so possible join. */
- if (sad->gesture_dir == 'n') {
+ if (sad->gesture_dir == SCREEN_DIR_N) {
WM_cursor_set(win, WM_CURSOR_N_ARROW);
}
- else if (sad->gesture_dir == 's') {
+ else if (sad->gesture_dir == SCREEN_DIR_S) {
WM_cursor_set(win, WM_CURSOR_S_ARROW);
}
- else if (sad->gesture_dir == 'e') {
+ else if (sad->gesture_dir == SCREEN_DIR_E) {
WM_cursor_set(win, WM_CURSOR_E_ARROW);
}
else {
+ BLI_assert(sad->gesture_dir == SCREEN_DIR_W);
WM_cursor_set(win, WM_CURSOR_W_ARROW);
}
is_gesture = (delta_max > join_threshold);
@@ -1350,6 +1362,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
area->winx,
area->winy,
SPACE_EMPTY,
+ false,
true,
false,
WIN_ALIGN_ABSOLUTE);
@@ -1387,6 +1400,58 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Area Close Operator
+ *
+ * Close selected area, replace by expanding a neighbor
+ * \{ */
+
+/* operator callback */
+static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
+{
+ ScrArea *area = CTX_wm_area(C);
+ if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) {
+ WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool area_close_poll(bContext *C)
+{
+ if (!ED_operator_areaactive(C)) {
+ return false;
+ }
+
+ ScrArea *area = CTX_wm_area(C);
+
+ if (ED_area_is_global(area)) {
+ return false;
+ }
+
+ bScreen *screen = CTX_wm_screen(C);
+
+ /* Can this area join with ANY other area? */
+ LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) {
+ if (area_getorientation(ar, area) != -1) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void SCREEN_OT_area_close(wmOperatorType *ot)
+{
+ ot->name = "Close Area";
+ ot->description = "Close selected area";
+ ot->idname = "SCREEN_OT_area_close";
+ ot->invoke = area_close_invoke;
+ ot->poll = area_close_poll;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Move Area Edge Operator
* \{ */
@@ -1420,7 +1485,7 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot)
typedef struct sAreaMoveData {
int bigger, smaller, origval, step;
- char dir;
+ eScreenAxis dir_axis;
enum AreaMoveSnapType {
/* Snapping disabled */
SNAP_NONE = 0,
@@ -1439,7 +1504,7 @@ typedef struct sAreaMoveData {
* need window bounds in order to get correct limits */
static void area_move_set_limits(wmWindow *win,
bScreen *screen,
- int dir,
+ const eScreenAxis dir_axis,
int *bigger,
int *smaller,
bool *use_bigger_smaller_snap)
@@ -1492,7 +1557,7 @@ static void area_move_set_limits(wmWindow *win,
WM_window_rect_calc(win, &window_rect);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
int areamin = ED_area_headersize();
if (area->v1->vec.y > window_rect.ymin) {
@@ -1555,8 +1620,8 @@ static bool area_move_init(bContext *C, wmOperator *op)
sAreaMoveData *md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
op->customdata = md;
- md->dir = screen_geom_edge_is_horizontal(actedge) ? 'h' : 'v';
- if (md->dir == 'h') {
+ md->dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_H : SCREEN_AXIS_V;
+ if (md->dir_axis == SCREEN_AXIS_H) {
md->origval = actedge->v1->vec.y;
}
else {
@@ -1571,7 +1636,8 @@ static bool area_move_init(bContext *C, wmOperator *op)
}
bool use_bigger_smaller_snap = false;
- area_move_set_limits(win, screen, md->dir, &md->bigger, &md->smaller, &use_bigger_smaller_snap);
+ area_move_set_limits(
+ win, screen, md->dir_axis, &md->bigger, &md->smaller, &use_bigger_smaller_snap);
md->snap_type = use_bigger_smaller_snap ? SNAP_BIGGER_SMALLER_ONLY : SNAP_AREAGRID;
@@ -1582,7 +1648,7 @@ static int area_snap_calc_location(const bScreen *screen,
const enum AreaMoveSnapType snap_type,
const int delta,
const int origval,
- const int dir,
+ const eScreenAxis dir_axis,
const int bigger,
const int smaller)
{
@@ -1607,7 +1673,7 @@ static int area_snap_calc_location(const bScreen *screen,
break;
case SNAP_FRACTION_AND_ADJACENT: {
- const int axis = (dir == 'v') ? 0 : 1;
+ const int axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1;
int snap_dist_best = INT_MAX;
{
const float div_array[] = {
@@ -1675,7 +1741,7 @@ static int area_snap_calc_location(const bScreen *screen,
static void area_move_apply_do(const bContext *C,
int delta,
const int origval,
- const int dir,
+ const eScreenAxis dir_axis,
const int bigger,
const int smaller,
const enum AreaMoveSnapType snap_type)
@@ -1693,11 +1759,12 @@ static void area_move_apply_do(const bContext *C,
final_loc = origval + delta;
}
else {
- final_loc = area_snap_calc_location(screen, snap_type, delta, origval, dir, bigger, smaller);
+ final_loc = area_snap_calc_location(
+ screen, snap_type, delta, origval, dir_axis, bigger, smaller);
}
BLI_assert(final_loc != -1);
- short axis = (dir == 'v') ? 0 : 1;
+ short axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1;
ED_screen_verts_iter(win, screen, v1)
{
@@ -1753,7 +1820,7 @@ static void area_move_apply(bContext *C, wmOperator *op)
sAreaMoveData *md = op->customdata;
int delta = RNA_int_get(op->ptr, "delta");
- area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->snap_type);
+ area_move_apply_do(C, delta, md->origval, md->dir_axis, md->bigger, md->smaller, md->snap_type);
}
static void area_move_exit(bContext *C, wmOperator *op)
@@ -1818,7 +1885,7 @@ static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
int x = RNA_int_get(op->ptr, "x");
int y = RNA_int_get(op->ptr, "y");
- int delta = (md->dir == 'v') ? event->x - x : event->y - y;
+ const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->x - x : event->y - y;
RNA_int_set(op->ptr, "delta", delta);
area_move_apply(C, op);
@@ -1884,7 +1951,7 @@ static void SCREEN_OT_area_move(wmOperatorType *ot)
/*
* operator state vars:
* fac spit point
- * dir direction 'v' or 'h'
+ * dir direction #SCREEN_AXIS_V or #SCREEN_AXIS_H
*
* operator customdata:
* area pointer to (active) area
@@ -1921,7 +1988,7 @@ typedef struct sAreaSplitData {
int delta; /* delta move edge */
int origmin, origsize; /* to calculate fac, for property storage */
int previewmode; /* draw previewline, then split */
- void *draw_callback; /* call `ED_screen_draw_split_preview` */
+ void *draw_callback; /* call `screen_draw_split_preview` */
bool do_snap;
ScrEdge *nedge; /* new edge */
@@ -1936,10 +2003,10 @@ static void area_split_draw_cb(const struct wmWindow *UNUSED(win), void *userdat
sAreaSplitData *sd = op->customdata;
if (sd->sarea) {
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
float fac = RNA_float_get(op->ptr, "factor");
- ED_screen_draw_split_preview(sd->sarea, dir, fac);
+ screen_draw_split_preview(sd->sarea, dir_axis, fac);
}
}
@@ -1966,14 +2033,18 @@ static bool area_split_init(bContext *C, wmOperator *op)
}
/* required properties */
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
/* minimal size */
- if (dir == 'v' && area->winx < 2 * AREAMINX) {
- return false;
+ if (dir_axis == SCREEN_AXIS_V) {
+ if (area->winx < 2 * AREAMINX) {
+ return false;
+ }
}
- if (dir == 'h' && area->winy < 2 * ED_area_headersize()) {
- return false;
+ else {
+ if (area->winy < 2 * ED_area_headersize()) {
+ return false;
+ }
}
/* custom data */
@@ -1981,7 +2052,7 @@ static bool area_split_init(bContext *C, wmOperator *op)
op->customdata = sd;
sd->sarea = area;
- if (dir == 'v') {
+ if (dir_axis == SCREEN_AXIS_V) {
sd->origmin = area->v1->vec.x;
sd->origsize = area->v4->vec.x - sd->origmin;
}
@@ -2030,9 +2101,9 @@ static bool area_split_apply(bContext *C, wmOperator *op)
sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
float fac = RNA_float_get(op->ptr, "factor");
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
- sd->narea = area_split(win, screen, sd->sarea, dir, fac, 0); /* 0 = no merge */
+ sd->narea = area_split(win, screen, sd->sarea, dir_axis, fac, false); /* false = no merge */
if (sd->narea == NULL) {
return false;
@@ -2049,7 +2120,7 @@ static bool area_split_apply(bContext *C, wmOperator *op)
sd->nedge->v1->editflag = 1;
sd->nedge->v2->editflag = 1;
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
sd->origval = sd->nedge->v1->vec.y;
}
else {
@@ -2098,8 +2169,8 @@ static void area_split_exit(bContext *C, wmOperator *op)
static void area_split_preview_update_cursor(bContext *C, wmOperator *op)
{
wmWindow *win = CTX_wm_window(C);
- int dir = RNA_enum_get(op->ptr, "direction");
- WM_cursor_set(win, dir == 'h' ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
+ WM_cursor_set(win, (dir_axis == SCREEN_AXIS_H) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
}
/* UI callback, adds new handler */
@@ -2115,7 +2186,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
PropertyRNA *prop_factor = RNA_struct_find_property(op->ptr, "factor");
PropertyRNA *prop_cursor = RNA_struct_find_property(op->ptr, "cursor");
- int dir;
+ eScreenAxis dir_axis;
if (event->type == EVT_ACTIONZONE_AREA) {
sActionzoneData *sad = event->customdata;
@@ -2143,12 +2214,12 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float factor;
/* Prepare operator state vars. */
- if (ELEM(sad->gesture_dir, 'n', 's')) {
- dir = 'h';
+ if (SCREEN_DIR_IS_VERTICAL(sad->gesture_dir)) {
+ dir_axis = SCREEN_AXIS_H;
factor = factor_h;
}
else {
- dir = 'v';
+ dir_axis = SCREEN_AXIS_V;
factor = factor_v;
}
@@ -2158,7 +2229,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
RNA_property_float_set(op->ptr, prop_factor, factor);
- RNA_property_enum_set(op->ptr, prop_dir, dir);
+ RNA_property_enum_set(op->ptr, prop_dir, dir_axis);
/* general init, also non-UI case, adds customdata, sets area and defaults */
if (!area_split_init(C, op)) {
@@ -2170,8 +2241,8 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (area == NULL) {
return OPERATOR_CANCELLED;
}
- dir = RNA_property_enum_get(op->ptr, prop_dir);
- if (dir == 'h') {
+ dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
+ if (dir_axis == SCREEN_AXIS_H) {
RNA_property_float_set(
op->ptr, prop_factor, ((float)(event->x - area->v1->vec.x)) / (float)area->winx);
}
@@ -2204,9 +2275,9 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- dir = screen_geom_edge_is_horizontal(actedge) ? 'v' : 'h';
+ dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_V : SCREEN_AXIS_H;
- RNA_property_enum_set(op->ptr, prop_dir, dir);
+ RNA_property_enum_set(op->ptr, prop_dir, dir_axis);
/* special case, adds customdata, sets defaults */
if (!area_split_menu_init(C, op)) {
@@ -2219,7 +2290,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (event->type == EVT_ACTIONZONE_AREA) {
/* do the split */
if (area_split_apply(C, op)) {
- area_move_set_limits(win, screen, dir, &sd->bigger, &sd->smaller, NULL);
+ area_move_set_limits(win, screen, dir_axis, &sd->bigger, &sd->smaller, NULL);
/* add temp handler for edge move or cancel */
G.moving |= G_TRANSFORM_WM;
@@ -2307,8 +2378,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
else {
if (event->val == KM_PRESS) {
if (sd->sarea) {
- int dir = RNA_property_enum_get(op->ptr, prop_dir);
- RNA_property_enum_set(op->ptr, prop_dir, (dir == 'v') ? 'h' : 'v');
+ const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
+ RNA_property_enum_set(
+ op->ptr, prop_dir, (dir_axis == SCREEN_AXIS_V) ? SCREEN_AXIS_H : SCREEN_AXIS_V);
area_split_preview_update_cursor(C, op);
update_factor = true;
}
@@ -2329,9 +2401,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (update_factor) {
- const int dir = RNA_property_enum_get(op->ptr, prop_dir);
+ const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
- sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval;
+ sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->x - sd->origval : event->y - sd->origval;
if (sd->previewmode == 0) {
if (sd->do_snap) {
@@ -2339,12 +2411,12 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
SNAP_FRACTION_AND_ADJACENT,
sd->delta,
sd->origval,
- dir,
+ dir_axis,
sd->bigger,
sd->smaller);
sd->delta = snap_loc - sd->origval;
}
- area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, SNAP_NONE);
+ area_move_apply_do(C, sd->delta, sd->origval, dir_axis, sd->bigger, sd->smaller, SNAP_NONE);
}
else {
if (sd->sarea) {
@@ -2355,7 +2427,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (sd->sarea) {
ScrArea *area = sd->sarea;
- if (dir == 'v') {
+ if (dir_axis == SCREEN_AXIS_V) {
sd->origmin = area->v1->vec.x;
sd->origsize = area->v4->vec.x - sd->origmin;
}
@@ -2371,7 +2443,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
SNAP_FRACTION_AND_ADJACENT,
sd->delta,
sd->origval,
- dir,
+ dir_axis,
sd->origmin + sd->origsize,
-sd->origmin);
@@ -2393,8 +2465,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
static const EnumPropertyItem prop_direction_items[] = {
- {'h', "HORIZONTAL", 0, "Horizontal", ""},
- {'v', "VERTICAL", 0, "Vertical", ""},
+ {SCREEN_AXIS_H, "HORIZONTAL", 0, "Horizontal", ""},
+ {SCREEN_AXIS_V, "VERTICAL", 0, "Vertical", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -2415,7 +2487,7 @@ static void SCREEN_OT_area_split(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* rna */
- RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
+ RNA_def_enum(ot->srna, "direction", prop_direction_items, SCREEN_AXIS_H, "Direction", "");
RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
RNA_def_int_vector(
ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
@@ -3210,9 +3282,10 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
*/
typedef struct sAreaJoinData {
- ScrArea *sa1; /* first area to be considered */
- ScrArea *sa2; /* second area to be considered */
- void *draw_callback; /* call `ED_screen_draw_join_shape` */
+ ScrArea *sa1; /* Potential source area (kept). */
+ ScrArea *sa2; /* Potential target area (removed or reduced). */
+ eScreenDir dir; /* Direction of potential join. */
+ void *draw_callback; /* call #screen_draw_join_highlight */
} sAreaJoinData;
@@ -3221,8 +3294,8 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata
const wmOperator *op = userdata;
sAreaJoinData *sd = op->customdata;
- if (sd->sa1 && sd->sa2) {
- ED_screen_draw_join_shape(sd->sa1, sd->sa2);
+ if (sd->sa1 && sd->sa2 && (sd->dir != SCREEN_DIR_NONE)) {
+ screen_draw_join_highlight(sd->sa1, sd->sa2);
}
}
@@ -3244,6 +3317,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s
jd->sa1 = sa1;
jd->sa2 = sa2;
+ jd->dir = SCREEN_DIR_NONE;
op->customdata = jd;
@@ -3256,7 +3330,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s
static bool area_join_apply(bContext *C, wmOperator *op)
{
sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
- if (!jd) {
+ if (!jd || (jd->dir == SCREEN_DIR_NONE)) {
return false;
}
@@ -3358,61 +3432,30 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
case MOUSEMOVE: {
ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y);
- int dir = -1;
+ jd->dir = area_getorientation(jd->sa1, jd->sa2);
- if (area) {
- if (jd->sa1 != area) {
- dir = area_getorientation(jd->sa1, area);
- if (dir != -1) {
- jd->sa2 = area;
- }
- else {
- /* we are not bordering on the previously selected area
- * we check if area has common border with the one marked for removal
- * in this case we can swap areas.
- */
- dir = area_getorientation(area, jd->sa2);
- if (dir != -1) {
- jd->sa1 = jd->sa2;
- jd->sa2 = area;
- }
- else {
- jd->sa2 = NULL;
- }
- }
- WM_event_add_notifier(C, NC_WINDOW, NULL);
- }
- else {
- /* we are back in the area previously selected for keeping
- * we swap the areas if possible to allow user to choose */
- if (jd->sa2 != NULL) {
- jd->sa1 = jd->sa2;
- jd->sa2 = area;
- dir = area_getorientation(jd->sa1, jd->sa2);
- if (dir == -1) {
- printf("oops, didn't expect that!\n");
- }
- }
- else {
- dir = area_getorientation(jd->sa1, area);
- if (dir != -1) {
- jd->sa2 = area;
- }
- }
- WM_event_add_notifier(C, NC_WINDOW, NULL);
- }
+ if (area == jd->sa1) {
+ /* Hovering current source, so change direction. */
+ jd->sa1 = jd->sa2;
+ jd->sa2 = area;
+ jd->dir = area_getorientation(jd->sa1, jd->sa2);
}
+ else if (area != jd->sa2) {
+ jd->dir = SCREEN_DIR_NONE;
+ }
+
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
- if (dir == 1) {
+ if (jd->dir == SCREEN_DIR_N) {
WM_cursor_set(win, WM_CURSOR_N_ARROW);
}
- else if (dir == 3) {
+ else if (jd->dir == SCREEN_DIR_S) {
WM_cursor_set(win, WM_CURSOR_S_ARROW);
}
- else if (dir == 2) {
+ else if (jd->dir == SCREEN_DIR_E) {
WM_cursor_set(win, WM_CURSOR_E_ARROW);
}
- else if (dir == 0) {
+ else if (jd->dir == SCREEN_DIR_W) {
WM_cursor_set(win, WM_CURSOR_W_ARROW);
}
else {
@@ -3423,6 +3466,10 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
case LEFTMOUSE:
if (event->val == KM_RELEASE) {
+ if (jd->dir == SCREEN_DIR_NONE) {
+ area_join_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
ED_area_tag_redraw(jd->sa1);
ED_area_tag_redraw(jd->sa2);
@@ -3493,7 +3540,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent
&ptr);
/* store initial mouse cursor position. */
RNA_int_set_array(&ptr, "cursor", &event->x);
- RNA_enum_set(&ptr, "direction", 'v');
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V);
/* Horizontal Split */
uiItemFullO(layout,
@@ -3506,7 +3553,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent
&ptr);
/* store initial mouse cursor position. */
RNA_int_set_array(&ptr, "cursor", &event->x);
- RNA_enum_set(&ptr, "direction", 'h');
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H);
if (sa1 && sa2) {
uiItemS(layout);
@@ -4069,6 +4116,69 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot)
/** \name Region Context Menu Operator (Header/Footer/Navbar)
* \{ */
+static void screen_area_menu_items(ScrArea *area, uiLayout *layout)
+{
+ if (ED_area_is_global(area)) {
+ return;
+ }
+
+ PointerRNA ptr;
+
+ /* Mouse position as if in middle of area. */
+ const int loc[2] = {BLI_rcti_cent_x(&area->totrct), BLI_rcti_cent_y(&area->totrct)};
+
+ /* Vertical Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Vertical Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+
+ RNA_int_set_array(&ptr, "cursor", loc);
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V);
+
+ /* Horizontal Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Horizontal Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+
+ RNA_int_set_array(&ptr, "cursor", &loc[0]);
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H);
+
+ uiItemS(layout);
+
+ if (area->spacetype != SPACE_FILE) {
+ uiItemO(layout,
+ area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"),
+ ICON_NONE,
+ "SCREEN_OT_screen_full_area");
+
+ if (!area->full) {
+ uiItemFullO(layout,
+ "SCREEN_OT_screen_full_area",
+ IFACE_("Full Screen Area"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+ RNA_boolean_set(&ptr, "use_hide_panels", true);
+ }
+ }
+
+ uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_dupli");
+ uiItemS(layout);
+ uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_close");
+}
+
void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
{
ScrArea *area = CTX_wm_area(C);
@@ -4102,17 +4212,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN
if (!ELEM(area->spacetype, SPACE_TOPBAR)) {
uiItemS(layout);
-
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
- }
-
- /* File browser should be fullscreen all the time, top-bar should
- * never be. But other regions can be maximized/restored. */
- if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
uiItemS(layout);
-
- const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area");
- uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area");
+ screen_area_menu_items(area, layout);
}
}
@@ -4134,14 +4236,8 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
- /* File browser should be fullscreen all the time, top-bar should
- * never be. But other regions can be maximized/restored... */
- if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
- uiItemS(layout);
-
- const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area");
- uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area");
- }
+ uiItemS(layout);
+ screen_area_menu_items(area, layout);
}
void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
@@ -4351,9 +4447,16 @@ static void screen_animation_region_tag_redraw(ScrArea *area,
/* No need to do a full redraw as the current frame indicator is only updated.
* We do need to redraw when this area is in full screen as no other areas
* will be tagged for redrawing. */
- if ((region->regiontype == RGN_TYPE_WINDOW) &&
- (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) && !area->full) {
- return;
+ if (region->regiontype == RGN_TYPE_WINDOW && !area->full) {
+ if (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) {
+ return;
+ }
+
+ if (area->spacetype == SPACE_SEQ) {
+ if (!ED_space_sequencer_has_visible_animation_on_strip(scene)) {
+ return;
+ }
+ }
}
ED_region_tag_redraw(region);
}
@@ -4853,6 +4956,7 @@ static int userpref_show_exec(bContext *C, wmOperator *op)
sizey,
SPACE_USERPREF,
false,
+ false,
true,
WIN_ALIGN_LOCATION_CENTER) != NULL) {
/* The header only contains the editor switcher and looks empty.
@@ -4863,6 +4967,11 @@ static int userpref_show_exec(bContext *C, wmOperator *op)
region->flag |= RGN_FLAG_HIDDEN;
ED_region_visibility_change_update(C, area, region);
+ /* And also show the region with "Load & Save" buttons. */
+ region = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
+ region->flag &= ~RGN_FLAG_HIDDEN;
+ ED_region_visibility_change_update(C, area, region);
+
return OPERATOR_FINISHED;
}
BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
@@ -4878,7 +4987,7 @@ static void SCREEN_OT_userpref_show(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = userpref_show_exec;
- ot->poll = ED_operator_screenactive;
+ ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */
}
/** \} */
@@ -4914,6 +5023,7 @@ static int drivers_editor_show_exec(bContext *C, wmOperator *op)
sizey,
SPACE_GRAPH,
false,
+ false,
true,
WIN_ALIGN_LOCATION_CENTER) != NULL) {
ED_drivers_editor_init(C, CTX_wm_area(C));
@@ -4955,7 +5065,7 @@ static void SCREEN_OT_drivers_editor_show(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = drivers_editor_show_exec;
- ot->poll = ED_operator_screenactive;
+ ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */
}
/** \} */
@@ -4982,6 +5092,7 @@ static int info_log_show_exec(bContext *C, wmOperator *op)
sizey,
SPACE_INFO,
false,
+ false,
true,
WIN_ALIGN_LOCATION_CENTER) != NULL) {
return OPERATOR_FINISHED;
@@ -4999,7 +5110,7 @@ static void SCREEN_OT_info_log_show(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = info_log_show_exec;
- ot->poll = ED_operator_screenactive;
+ ot->poll = ED_operator_screenactive_nobackground;
}
/** \} */
@@ -5331,7 +5442,7 @@ static void context_cycle_prop_get(bScreen *screen,
static int space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- const int direction = RNA_enum_get(op->ptr, "direction");
+ const eScreenCycle direction = RNA_enum_get(op->ptr, "direction");
PointerRNA ptr;
PropertyRNA *prop;
@@ -5380,7 +5491,7 @@ static int space_workspace_cycle_invoke(bContext *C, wmOperator *op, const wmEve
}
Main *bmain = CTX_data_main(C);
- const int direction = RNA_enum_get(op->ptr, "direction");
+ const eScreenCycle direction = RNA_enum_get(op->ptr, "direction");
WorkSpace *workspace_src = WM_window_get_active_workspace(win);
WorkSpace *workspace_dst = NULL;
@@ -5456,6 +5567,7 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(SCREEN_OT_area_move);
WM_operatortype_append(SCREEN_OT_area_split);
WM_operatortype_append(SCREEN_OT_area_join);
+ WM_operatortype_append(SCREEN_OT_area_close);
WM_operatortype_append(SCREEN_OT_area_options);
WM_operatortype_append(SCREEN_OT_area_dupli);
WM_operatortype_append(SCREEN_OT_area_swap);
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index ff0dab7f1c7..6df96b1e30f 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -213,9 +213,8 @@ static void screenshot_draw(bContext *UNUSED(C), wmOperator *op)
uiTemplateImageSettings(layout, &ptr, false);
/* main draw call */
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
}
static bool screenshot_poll(bContext *C)
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index b44f2f66d92..7e111905883 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -545,6 +545,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
int x,
int y,
float zoom,
+ const ePaintMode mode,
bool col,
bool primary)
{
@@ -556,6 +557,13 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
(brush->overlay_flags & BRUSH_OVERLAY_SECONDARY) != 0);
int overlay_alpha = (primary) ? brush->texture_overlay_alpha : brush->mask_overlay_alpha;
+ if (mode == PAINT_MODE_TEXTURE_3D) {
+ if (primary && brush->imagepaint_tool != PAINT_TOOL_DRAW) {
+ /* All non-draw tools don't use the primary texture (clone, smear, soften.. etc). */
+ return false;
+ }
+ }
+
if (!(mtex->tex) ||
!((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) ||
(valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) {
@@ -785,10 +793,11 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
/* Colored overlay should be drawn separately. */
if (col) {
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) {
- alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, mode, true, true);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) {
- alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false);
+ alpha_overlay_active = paint_draw_tex_overlay(
+ ups, brush, vc, x, y, zoom, mode, false, false);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
@@ -796,7 +805,7 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
}
else {
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) {
- alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, mode, false, true);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
@@ -1030,7 +1039,7 @@ static void cursor_draw_point_screen_space(const uint gpuattr,
float translation_vertex_cursor[3], location[3];
copy_v3_v3(location, true_location);
mul_m4_v3(obmat, location);
- ED_view3d_project(region, location, translation_vertex_cursor);
+ ED_view3d_project_v3(region, location, translation_vertex_cursor);
/* Do not draw points behind the view. Z [near, far] is mapped to [-1, 1]. */
if (translation_vertex_cursor[2] <= 1.0f) {
imm_draw_circle_fill_3d(
@@ -1315,6 +1324,13 @@ static bool paint_cursor_context_init(bContext *C,
copy_v3_fl(pcontext->outline_col, 0.8f);
}
+ const bool is_brush_tool = PAINT_brush_tool_poll(C);
+ if (!is_brush_tool) {
+ /* Use a default color for tools that are not brushes. */
+ pcontext->outline_alpha = 0.8f;
+ copy_v3_fl(pcontext->outline_col, 0.8f);
+ }
+
pcontext->is_stroke_active = pcontext->ups->stroke_active;
return true;
@@ -1601,9 +1617,11 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
pcontext->radius);
}
+ const bool is_brush_tool = PAINT_brush_tool_poll(pcontext->C);
+
/* Pose brush updates and rotation origins. */
- if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) {
/* Just after switching to the Pose Brush, the active vertex can be the same and the
* cursor won't be tagged to update, so always initialize the preview chain if it is
* null before drawing it. */
@@ -1636,7 +1654,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
2);
}
- if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
paint_cursor_preview_boundary_data_update(pcontext, update_previews);
paint_cursor_preview_boundary_data_pivot_draw(pcontext);
}
@@ -1657,17 +1675,18 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
GPU_matrix_mul(pcontext->vc.obact->obmat);
/* Drawing Cursor overlays in 3D object space. */
- if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_GRAB &&
+ (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) {
SCULPT_geometry_preview_lines_update(pcontext->C, pcontext->ss, pcontext->radius);
sculpt_geometry_preview_lines_draw(
pcontext->pos, pcontext->brush, pcontext->is_multires, pcontext->ss);
}
- if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) {
paint_cursor_pose_brush_segments_draw(pcontext);
}
- if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
SCULPT_boundary_edges_preview_draw(
pcontext->pos, pcontext->ss, pcontext->outline_col, pcontext->outline_alpha);
SCULPT_boundary_pivot_line_preview_draw(pcontext->pos, pcontext->ss);
@@ -1683,7 +1702,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
paint_cursor_draw_main_inactive_cursor(pcontext);
/* Cloth brush local simulation areas. */
- if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
brush->cloth_simulation_area_type != BRUSH_CLOTH_SIMULATION_AREA_GLOBAL) {
const float white[3] = {1.0f, 1.0f, 1.0f};
const float zero_v[3] = {0.0f};
@@ -1695,7 +1714,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
}
/* Layer brush height. */
- if (brush->sculpt_tool == SCULPT_TOOL_LAYER) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_LAYER) {
SCULPT_layer_brush_height_preview_draw(pcontext->pos,
brush,
pcontext->radius,
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 530689ce049..41db79bc134 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -300,7 +300,7 @@ typedef struct ProjPaintState {
/** Calculated from screenMin & screenMax. */
float screen_width;
float screen_height;
- /** from the carea or from the projection render. */
+ /** From the area or from the projection render. */
int winx, winy;
/* options for projection painting */
@@ -5571,7 +5571,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po
}
if (ps->thread_tot > 1) {
- task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH);
+ task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
image_pool = BKE_image_pool_new();
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 3ca0d853d6a..7341d984c91 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -87,7 +87,7 @@ struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke);
void *paint_stroke_mode_data(struct PaintStroke *stroke);
float paint_stroke_distance_get(struct PaintStroke *stroke);
void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data);
-bool paint_poll(struct bContext *C);
+bool PAINT_brush_tool_poll(struct bContext *C);
void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C));
void paint_cursor_delete_textures(void);
@@ -304,7 +304,7 @@ bool paint_curve_poll(struct bContext *C);
bool facemask_paint_poll(struct bContext *C);
void flip_v3_v3(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
-void flip_qt_qt(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
+void flip_qt_qt(float out[4], const float in[4], const enum ePaintSymmetryFlags symm);
/* stroke operator */
typedef enum BrushStrokeMode {
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 7671f69ee05..da34723eed4 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -1239,10 +1239,9 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
}));
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
BMLoop *(*looptris)[3];
looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
+ BM_mesh_calc_tessellation_beauty(bm, looptris);
BMIter iter;
int i;
@@ -1290,7 +1289,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
break;
}
BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode);
+ bm, looptris, looptris_tot, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode);
}
MEM_freeN(looptris);
@@ -1650,7 +1649,7 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_lasso_modal;
ot->exec = paint_mask_gesture_lasso_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1671,7 +1670,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_box_modal;
ot->exec = paint_mask_gesture_box_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1692,7 +1691,7 @@ void PAINT_OT_mask_line_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_straightline_oneshot_modal;
ot->exec = paint_mask_gesture_line_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1713,6 +1712,8 @@ void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_lasso_modal;
ot->exec = face_set_gesture_lasso_exec;
+ ot->poll = SCULPT_mode_poll_view3d;
+
/* Properties. */
WM_operator_properties_gesture_lasso(ot);
sculpt_gesture_operator_properties(ot);
@@ -1728,7 +1729,7 @@ void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_box_modal;
ot->exec = face_set_gesture_box_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1747,7 +1748,7 @@ void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_lasso_modal;
ot->exec = sculpt_trim_gesture_lasso_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1768,7 +1769,7 @@ void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_box_modal;
ot->exec = sculpt_trim_gesture_box_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1789,7 +1790,7 @@ void SCULPT_OT_project_line_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_straightline_oneshot_modal;
ot->exec = project_gesture_line_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index e1dc8fa30b9..fed89e02e8f 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -139,13 +139,14 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
+ const bool is_gpencil = (brush && brush->gpencil_settings != NULL);
// Object *ob = CTX_data_active_object(C);
float scalar = RNA_float_get(op->ptr, "scalar");
if (brush) {
/* pixel radius */
{
- const int old_size = BKE_brush_size_get(scene, brush);
+ const int old_size = (!is_gpencil) ? BKE_brush_size_get(scene, brush) : brush->size;
int size = (int)(scalar * old_size);
if (abs(old_size - size) < U.pixelsize) {
@@ -156,6 +157,12 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op)
size -= U.pixelsize;
}
}
+ /* Grease Pencil does not use unified size. */
+ if (is_gpencil) {
+ brush->size = max_ii(size, 1);
+ WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
+ return OPERATOR_FINISHED;
+ }
BKE_brush_size_set(scene, brush, size);
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 57b1102219e..59a5ad63f0e 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -846,7 +846,7 @@ static int paint_space_stroke(bContext *C,
while (length > 0.0f) {
float spacing = paint_space_stroke_spacing_variable(
C, scene, stroke, pressure, dpressure, length);
- float mouse[3];
+ float mouse[2];
if (length >= spacing) {
if (use_scene_spacing) {
@@ -856,7 +856,7 @@ static int paint_space_stroke(bContext *C,
add_v3_v3v3(final_world_space_position,
stroke->last_world_space_position,
final_world_space_position);
- ED_view3d_project(region, final_world_space_position, mouse);
+ ED_view3d_project_v2(region, final_world_space_position, mouse);
}
else {
mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
@@ -1184,14 +1184,43 @@ static void paint_line_strokes_spacing(bContext *C,
const float new_pos[2])
{
UnifiedPaintSettings *ups = stroke->ups;
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ Brush *brush = BKE_paint_brush(paint);
+ ePaintMode mode = BKE_paintmode_get_active_from_context(C);
+ ARegion *region = CTX_wm_region(C);
+
+ const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
float mouse[2], dmouse[2];
float length;
+ float d_world_space_position[3] = {0.0f};
+ float world_space_position_old[3], world_space_position_new[3];
- sub_v2_v2v2(dmouse, new_pos, old_pos);
copy_v2_v2(stroke->last_mouse_position, old_pos);
- length = normalize_v2(dmouse);
+ if (use_scene_spacing) {
+ bool hit_old = SCULPT_stroke_get_location(C, world_space_position_old, old_pos);
+ bool hit_new = SCULPT_stroke_get_location(C, world_space_position_new, new_pos);
+ mul_m4_v3(stroke->vc.obact->obmat, world_space_position_old);
+ mul_m4_v3(stroke->vc.obact->obmat, world_space_position_new);
+ if (hit_old && hit_new && stroke->stroke_over_mesh) {
+ sub_v3_v3v3(d_world_space_position, world_space_position_new, world_space_position_old);
+ length = len_v3(d_world_space_position);
+ stroke->stroke_over_mesh = true;
+ }
+ else {
+ length = 0.0f;
+ zero_v3(d_world_space_position);
+ stroke->stroke_over_mesh = hit_new;
+ if (stroke->stroke_over_mesh) {
+ copy_v3_v3(stroke->last_world_space_position, world_space_position_old);
+ }
+ }
+ }
+ else {
+ sub_v2_v2v2(dmouse, new_pos, old_pos);
+ length = normalize_v2(dmouse);
+ }
BLI_assert(length >= 0.0f);
@@ -1205,8 +1234,18 @@ static void paint_line_strokes_spacing(bContext *C,
*length_residue = 0.0;
if (length >= spacing) {
- mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final;
- mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final;
+ if (use_scene_spacing) {
+ float final_world_space_position[3];
+ normalize_v3(d_world_space_position);
+ mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final);
+ add_v3_v3v3(
+ final_world_space_position, world_space_position_old, final_world_space_position);
+ ED_view3d_project_v2(region, final_world_space_position, mouse);
+ }
+ else {
+ mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final;
+ mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final;
+ }
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0);
@@ -1302,6 +1341,13 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
if (!stroke->stroke_started) {
stroke->last_pressure = 1.0;
copy_v2_v2(stroke->last_mouse_position, data + 2 * j);
+
+ if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) {
+ stroke->stroke_over_mesh = SCULPT_stroke_get_location(
+ C, stroke->last_world_space_position, data + 2 * j);
+ mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
+ }
+
stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position);
if (stroke->stroke_started) {
@@ -1416,7 +1462,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (paint_supports_smooth_stroke(br, mode)) {
stroke->stroke_cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_smooth_cursor, stroke);
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_smooth_cursor, stroke);
}
stroke->stroke_init = true;
@@ -1443,7 +1489,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (br->flag & BRUSH_LINE) {
stroke->stroke_cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_line_cursor, stroke);
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_line_cursor, stroke);
}
first_dab = true;
@@ -1613,7 +1659,7 @@ void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data)
stroke->mode_data = mode_data;
}
-bool paint_poll(bContext *C)
+bool PAINT_brush_tool_poll(bContext *C)
{
Paint *p = BKE_paint_get_active_from_context(C);
Object *ob = CTX_data_active_object(C);
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index fc52f6fea7c..daccc6f228a 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -769,8 +769,8 @@ static void do_weight_paint_vertex_single(
MDeformVert *dv_mirr;
MDeformWeight *dw_mirr;
- /* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ /* Check if we should mirror vertex groups (X-axis). */
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology);
vgroup_mirr = wpi->mirror.index;
@@ -979,8 +979,8 @@ static void do_weight_paint_vertex_multi(
float curw, curw_real, oldw, neww, change, curw_mirr, change_mirr;
float dw_rel_free, dw_rel_locked;
- /* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ /* Check if we should mirror vertex groups (X-axis). */
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology);
if (!ELEM(index_mirr, -1, index)) {
@@ -1629,7 +1629,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
int i;
bDeformGroup *dg;
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
BKE_object_defgroup_mirror_selection(
ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
}
@@ -2191,7 +2191,7 @@ static void wpaint_paint_leaves(bContext *C,
/* NOTE: current mirroring code cannot be run in parallel */
TaskParallelSettings settings;
- const bool use_threading = ((me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) == 0);
+ const bool use_threading = !ME_USING_MIRROR_X_VERTEX_GROUPS(me);
BKE_pbvh_parallel_range_settings(&settings, use_threading, totnode);
switch ((eBrushWeightPaintTool)brush->weightpaint_tool) {
@@ -2322,6 +2322,13 @@ static void wpaint_do_symmetrical_brush_actions(
cache->symmetry = symm;
+ if (me->editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) {
+ /* We don't do any symmetry strokes when mirroring vertex groups. */
+ copy_v3_v3(cache->true_last_location, cache->true_location);
+ cache->is_last_valid = true;
+ return;
+ }
+
/* symm is a bit combination of XYZ - 1 is mirror
* X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (i = 1; i <= symm; i++) {
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
index 8277b485578..0fafd3589fe 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -235,7 +235,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
vc.obact, defbase_tot, &defbase_tot_sel);
if (defbase_tot_sel > 1) {
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
BKE_object_defgroup_mirror_selection(
vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
}
@@ -461,7 +461,7 @@ static bool weight_paint_set(Object *ob, float paintweight)
vgroup_active = ob->actdef - 1;
/* if mirror painting, find the other group */
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
vgroup_mirror = ED_wpaint_mirror_vgroup_ensure(ob, vgroup_active);
}
@@ -489,7 +489,8 @@ static bool weight_paint_set(Object *ob, float paintweight)
dw_prev->weight = dw->weight; /* set the undo weight */
dw->weight = paintweight;
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) { /* x mirror painting */
+ if (me->symmetry & ME_SYMMETRY_X) {
+ /* x mirror painting */
int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology);
if (j >= 0) {
/* copy, not paint again */
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
index a8ba87ac483..d6a118bbd59 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
@@ -118,7 +118,7 @@ bool ED_wpaint_ensure_data(bContext *C,
}
if (flag & WPAINT_ENSURE_MIRROR) {
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
int mirror = ED_wpaint_mirror_vgroup_ensure(ob, ob->actdef - 1);
if (vgroup_index) {
vgroup_index->mirror = mirror;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 964e5bdaa90..d6d54a1985d 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -780,6 +780,10 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
iter->neighbors = iter->neighbors_fixed;
for (int i = 0; i < ss->pmap[index].count; i++) {
+ if (ss->face_sets[vert_map->indices[i]] < 0) {
+ /* Skip connectivity from hidden faces. */
+ continue;
+ }
const MPoly *p = &ss->mpoly[vert_map->indices[i]];
uint f_adj_v[2];
if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
@@ -1850,7 +1854,7 @@ static void flip_v3(float v[3], const ePaintSymmetryFlags symm)
flip_v3_v3(v, v, symm);
}
-static void flip_qt(float quat[3], const ePaintSymmetryFlags symm)
+static void flip_qt(float quat[4], const ePaintSymmetryFlags symm)
{
flip_qt_qt(quat, quat, symm);
}
@@ -4192,7 +4196,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3],
}
}
-void SCULPT_flip_quat_by_symm_area(float quat[3],
+void SCULPT_flip_quat_by_symm_area(float quat[4],
const ePaintSymmetryFlags symm,
const ePaintSymmetryAreas symmarea,
const float pivot[3])
@@ -6602,7 +6606,7 @@ bool SCULPT_poll_view3d(bContext *C)
bool SCULPT_poll(bContext *C)
{
- return SCULPT_mode_poll(C) && paint_poll(C);
+ return SCULPT_mode_poll(C) && PAINT_brush_tool_poll(C);
}
static const char *sculpt_tool_name(Sculpt *sd)
diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c
index 8b8ed42a694..40874375772 100644
--- a/source/blender/editors/sculpt_paint/sculpt_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_expand.c
@@ -273,7 +273,7 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
*/
static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f)
{
- if (ss->face_sets[f] <= 0) {
+ if (expand_cache->original_face_sets[f] <= 0) {
return false;
}
@@ -1398,6 +1398,13 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa
{
const int totfaces = ss->totfaces;
for (int i = 0; i < totfaces; i++) {
+ if (expand_cache->original_face_sets[i] <= 0) {
+ /* Do not modify hidden Face Sets, even when restoring the IDs state. */
+ continue;
+ }
+ if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
+ continue;
+ }
ss->face_sets[i] = expand_cache->initial_face_sets[i];
}
}
@@ -1892,13 +1899,22 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
* The faces that were using the `delete_id` Face Set are filled
* using the content from their neighbors.
*/
-static void sculpt_expand_delete_face_set_id(
- int *r_face_sets, Mesh *mesh, MeshElemMap *pmap, const int totface, const int delete_id)
+static void sculpt_expand_delete_face_set_id(int *r_face_sets,
+ SculptSession *ss,
+ ExpandCache *expand_cache,
+ Mesh *mesh,
+ const int delete_id)
{
+ const int totface = ss->totfaces;
+ MeshElemMap *pmap = ss->pmap;
+
/* Check that all the face sets IDs in the mesh are not equal to `delete_id`
* before attempting to delete it. */
bool all_same_id = true;
for (int i = 0; i < totface; i++) {
+ if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
+ continue;
+ }
if (r_face_sets[i] != delete_id) {
all_same_id = false;
break;
@@ -1921,6 +1937,7 @@ static void sculpt_expand_delete_face_set_id(
}
while (BLI_LINKSTACK_SIZE(queue)) {
+ bool any_updated = false;
while (BLI_LINKSTACK_SIZE(queue)) {
const int f_index = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
int other_id = delete_id;
@@ -1931,6 +1948,10 @@ static void sculpt_expand_delete_face_set_id(
for (int i = 0; i < vert_map->count; i++) {
const int neighbor_face_index = vert_map->indices[i];
+ if (expand_cache->original_face_sets[neighbor_face_index] <= 0) {
+ /* Skip picking IDs from hidden Face Sets. */
+ continue;
+ }
if (r_face_sets[neighbor_face_index] != delete_id) {
other_id = r_face_sets[neighbor_face_index];
}
@@ -1938,18 +1959,36 @@ static void sculpt_expand_delete_face_set_id(
}
if (other_id != delete_id) {
+ any_updated = true;
r_face_sets[f_index] = other_id;
}
else {
BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(f_index));
}
}
+ if (!any_updated) {
+ /* No Face Sets where updated in this iteration, which means that no more content to keep
+ * filling the polys of the deleted Face Set was found. Break to avoid entering an infinite
+ * loop trying to search for those polys again. */
+ break;
+ }
BLI_LINKSTACK_SWAP(queue, queue_next);
}
BLI_LINKSTACK_FREE(queue);
BLI_LINKSTACK_FREE(queue_next);
+
+ /* Ensure that the visibility state of the modified Face Sets is the same as the original ones.
+ */
+ for (int i = 0; i < totface; i++) {
+ if (expand_cache->original_face_sets[i] >= 0) {
+ r_face_sets[i] = abs(r_face_sets[i]);
+ }
+ else {
+ r_face_sets[i] = -abs(r_face_sets[i]);
+ }
+ }
}
static void sculpt_expand_cache_initial_config_set(bContext *C,
@@ -2070,9 +2109,9 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
if (ss->expand_cache->modify_active_face_set) {
sculpt_expand_delete_face_set_id(ss->expand_cache->initial_face_sets,
+ ss,
+ ss->expand_cache,
ob->data,
- ss->pmap,
- ss->totfaces,
ss->expand_cache->next_face_set);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 17c4beab086..bdbdb75732a 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -949,10 +949,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
MEM_SAFE_FREE(nodes);
- if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
- BKE_mesh_flush_hidden_from_verts(ob->data);
- }
-
SCULPT_tag_update_overlays(C);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 087cb6dd94a..e2ee4c9fed3 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -278,7 +278,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3],
const ePaintSymmetryFlags symm,
const ePaintSymmetryAreas symmarea,
const float pivot[3]);
-void SCULPT_flip_quat_by_symm_area(float quat[3],
+void SCULPT_flip_quat_by_symm_area(float quat[4],
const ePaintSymmetryFlags symm,
const ePaintSymmetryAreas symmarea,
const float pivot[3]);
diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index 4d2a1bf13dc..587ce346428 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -197,8 +197,9 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
mul_v3_fl(disp, segments[ik].weights[vd.index]);
/* Apply the vertex mask to the displacement. */
- float mask = vd.mask ? *vd.mask : 0.0f;
- mul_v3_fl(disp, 1.0f - mask);
+ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ mul_v3_fl(disp, mask * automask);
/* Accumulate the displacement. */
add_v3_v3(total_disp, disp);
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index b6205db6f45..71166b7c20c 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -1189,9 +1189,7 @@ static SculptUndoNode *sculpt_undo_geometry_push(Object *object, SculptUndoType
static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType type)
{
UndoSculpt *usculpt = sculpt_undo_get_nodes();
- SculptUndoNode *unode = usculpt->nodes.first;
-
- unode = MEM_callocN(sizeof(*unode), __func__);
+ SculptUndoNode *unode = MEM_callocN(sizeof(*unode), __func__);
BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
unode->type = type;
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 85616f6356d..fe0a53ae964 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -52,6 +52,7 @@
#include "RNA_enum_types.h"
#include "SEQ_iterator.h"
+#include "SEQ_utils.h"
#include "UI_interface.h"
@@ -258,7 +259,7 @@ static void sound_update_animation_flags(Scene *scene)
scene->id.tag |= LIB_TAG_DOIT;
SEQ_ALL_BEGIN (scene->ed, seq) {
- SEQ_iterator_recursive_apply(seq, sound_update_animation_flags_fn, scene);
+ SEQ_recursive_apply(seq, sound_update_animation_flags_fn, scene);
}
SEQ_ALL_END;
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 5c061518570..fea62f0d9c2 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -648,6 +648,19 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static char *actkeys_paste_description(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(op),
+ PointerRNA *ptr)
+{
+ /* Custom description if the 'flipped' option is used. */
+ if (RNA_boolean_get(ptr, "flipped")) {
+ return BLI_strdup("Paste keyframes from mirrored bones if they exist");
+ }
+
+ /* Use the default description in the other cases. */
+ return NULL;
+}
+
void ACTION_OT_paste(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -660,6 +673,7 @@ void ACTION_OT_paste(wmOperatorType *ot)
/* api callbacks */
// ot->invoke = WM_operator_props_popup; // better wait for action redo panel
+ ot->get_description = actkeys_paste_description;
ot->exec = actkeys_paste_exec;
ot->poll = ED_operator_action_active;
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 722005235d3..f6af2f79890 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -301,6 +301,11 @@ static void action_header_region_init(wmWindowManager *UNUSED(wm), ARegion *regi
static void action_header_region_draw(const bContext *C, ARegion *region)
{
+ /* The anim context is not actually used, but this makes sure the action being displayed is up to
+ * date. */
+ bAnimContext ac;
+ ANIM_animdata_get_context(C, &ac);
+
ED_region_header(C, region);
}
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index c71e5e49d8d..b5f6874fcfc 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -50,7 +50,7 @@ if(WITH_FREESTYLE)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
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 c42e2531f25..aeb2c04656e 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -975,23 +975,13 @@ int /*eContextResult*/ buttons_context(const bContext *C,
if (matnr < 0) {
matnr = 0;
}
- CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, &ob->mat[matnr]);
+ /* Keep aligned with rna_Object_material_slots_get. */
+ CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, POINTER_FROM_INT(matnr + 1));
}
}
return CTX_RESULT_OK;
}
- if (CTX_data_equals(member, "modifier")) {
- PointerRNA *ptr = get_pointer_type(path, &RNA_Modifier);
-
- if (ptr != NULL && !RNA_pointer_is_null(ptr)) {
- Object *ob = (Object *)ptr->owner_id;
- ModifierData *md = ptr->data;
- CTX_data_pointer_set(result, &ob->id, &RNA_Modifier, md);
- return CTX_RESULT_OK;
- }
- return CTX_RESULT_NO_DATA;
- }
if (CTX_data_equals(member, "texture_user")) {
ButsContextTexture *ct = sbuts->texuser;
diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h
index 74e7bc11c26..7564fa4b930 100644
--- a/source/blender/editors/space_buttons/buttons_intern.h
+++ b/source/blender/editors/space_buttons/buttons_intern.h
@@ -35,6 +35,7 @@ struct bContext;
struct bContextDataResult;
struct bNode;
struct bNodeTree;
+struct bNodeSocket;
struct wmOperatorType;
struct SpaceProperties_Runtime {
@@ -66,6 +67,7 @@ typedef struct ButsTextureUser {
struct bNodeTree *ntree;
struct bNode *node;
+ struct bNodeSocket *socket;
const char *category;
int icon;
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index 43128ed00fa..97e3cb750c1 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -75,15 +75,16 @@ static SpaceProperties *find_space_properties(const bContext *C);
/************************* Texture User **************************/
-static void buttons_texture_user_node_property_add(ListBase *users,
- ID *id,
- PointerRNA ptr,
- PropertyRNA *prop,
- bNodeTree *ntree,
- bNode *node,
- const char *category,
- int icon,
- const char *name)
+static void buttons_texture_user_socket_property_add(ListBase *users,
+ ID *id,
+ PointerRNA ptr,
+ PropertyRNA *prop,
+ bNodeTree *ntree,
+ bNode *node,
+ bNodeSocket *socket,
+ const char *category,
+ int icon,
+ const char *name)
{
ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
@@ -92,6 +93,7 @@ static void buttons_texture_user_node_property_add(ListBase *users,
user->prop = prop;
user->ntree = ntree;
user->node = node;
+ user->socket = socket;
user->category = category;
user->icon = icon;
user->name = name;
@@ -181,25 +183,29 @@ static void buttons_texture_modifier_geonodes_users_add(Object *ob,
/* Recurse into the node group */
buttons_texture_modifier_geonodes_users_add(ob, nmd, (bNodeTree *)node->id, users);
}
- else if (node->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) {
- RNA_pointer_create(&node_tree->id, &RNA_Node, node, &ptr);
- prop = RNA_struct_find_property(&ptr, "texture");
- if (prop == NULL) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ if (socket->type != SOCK_TEXTURE) {
continue;
}
+ RNA_pointer_create(&node_tree->id, &RNA_NodeSocket, socket, &ptr);
+ prop = RNA_struct_find_property(&ptr, "default_value");
PointerRNA texptr = RNA_property_pointer_get(&ptr, prop);
Tex *tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? (Tex *)texptr.data : NULL;
if (tex != NULL) {
- buttons_texture_user_node_property_add(users,
- &ob->id,
- ptr,
- prop,
- node_tree,
- node,
- N_("Geometry Nodes"),
- RNA_struct_ui_icon(ptr.type),
- nmd->modifier.name);
+ buttons_texture_user_socket_property_add(users,
+ &ob->id,
+ ptr,
+ prop,
+ node_tree,
+ node,
+ socket,
+ N_("Geometry Nodes"),
+ RNA_struct_ui_icon(ptr.type),
+ nmd->modifier.name);
}
}
}
diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c
index d555238e949..7379891543b 100644
--- a/source/blender/editors/space_clip/clip_buttons.c
+++ b/source/blender/editors/space_clip/clip_buttons.c
@@ -809,12 +809,12 @@ void uiTemplateMovieclipInformation(uiLayout *layout,
char str[1024];
size_t ofs = 0;
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height);
if (ibuf) {
if (ibuf->rect_float) {
if (ibuf->channels != 4) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_(", %d float channel(s)"), ibuf->channels);
}
else if (ibuf->planes == R_IMF_PLANES_RGBA) {
@@ -837,7 +837,7 @@ void uiTemplateMovieclipInformation(uiLayout *layout,
short frs_sec;
float frs_sec_base;
if (IMB_anim_get_fps(clip->anim, &frs_sec, &frs_sec_base, true)) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_(", %.2f fps"), (float)frs_sec / frs_sec_base);
}
}
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index 2da13646a8b..9aa6a993c13 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -904,7 +904,7 @@ static void start_prefetch_threads(MovieClip *clip,
queue.do_update = do_update;
queue.progress = progress;
- TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW);
+ TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
for (int i = 0; i < tot_thread; i++) {
BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, NULL);
}
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 0c54a042a1a..369a6e7c944 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -1235,10 +1235,8 @@ static void do_movie_proxy(void *pjv,
float *progress)
{
ProxyJob *pj = pjv;
- Scene *scene = pj->scene;
MovieClip *clip = pj->clip;
struct MovieDistortion *distortion = NULL;
- int cfra, sfra = SFRA, efra = EFRA;
if (pj->index_context) {
IMB_anim_index_rebuild(pj->index_context, stop, do_update, progress);
@@ -1252,8 +1250,8 @@ static void do_movie_proxy(void *pjv,
return;
}
- sfra = 1;
- efra = clip->len;
+ const int sfra = 1;
+ const int efra = clip->len;
if (build_undistort_count) {
int threads = BLI_system_thread_count();
@@ -1265,7 +1263,7 @@ static void do_movie_proxy(void *pjv,
BKE_tracking_distortion_set_threads(distortion, threads);
}
- for (cfra = sfra; cfra <= efra; cfra++) {
+ for (int cfra = sfra; cfra <= efra; cfra++) {
BKE_movieclip_build_proxy_frame(
clip, pj->clip_flag, distortion, cfra, build_undistort_sizes, build_undistort_count, 1);
@@ -1431,7 +1429,7 @@ static void do_sequence_proxy(void *pjv,
queue.do_update = do_update;
queue.progress = progress;
- TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW);
+ TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles");
for (int i = 0; i < tot_thread; i++) {
ProxyThread *handle = &handles[i];
diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c
index 9882304d97d..0a99d1020f6 100644
--- a/source/blender/editors/space_clip/tracking_ops_track.c
+++ b/source/blender/editors/space_clip/tracking_ops_track.c
@@ -24,8 +24,11 @@
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
@@ -398,6 +401,28 @@ static int track_markers_modal(bContext *C, wmOperator *UNUSED(op), const wmEven
return OPERATOR_PASS_THROUGH;
}
+static char *track_markers_desc(bContext *UNUSED(C), wmOperatorType *UNUSED(op), PointerRNA *ptr)
+{
+ const bool backwards = RNA_boolean_get(ptr, "backwards");
+ const bool sequence = RNA_boolean_get(ptr, "sequence");
+
+ if (backwards && sequence) {
+ return BLI_strdup(TIP_("Track the selected markers backward for the entire clip"));
+ }
+ if (backwards && !sequence) {
+ return BLI_strdup(TIP_("Track the selected markers backward by one frame"));
+ }
+ if (!backwards && sequence) {
+ return BLI_strdup(TIP_("Track the selected markers forward for the entire clip"));
+ }
+ if (!backwards && !sequence) {
+ return BLI_strdup(TIP_("Track the selected markers forward by one frame"));
+ }
+
+ /* Use default description. */
+ return NULL;
+}
+
void CLIP_OT_track_markers(wmOperatorType *ot)
{
/* identifiers */
@@ -410,6 +435,7 @@ void CLIP_OT_track_markers(wmOperatorType *ot)
ot->invoke = track_markers_invoke;
ot->modal = track_markers_modal;
ot->poll = ED_space_clip_tracking_poll;
+ ot->get_description = track_markers_desc;
/* flags */
ot->flag = OPTYPE_UNDO;
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index c1dcf2e56d3..17d029f7541 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -142,7 +142,8 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh
color);
}
-static void file_draw_icon(uiBlock *block,
+static void file_draw_icon(const SpaceFile *sfile,
+ uiBlock *block,
const FileDirEntry *file,
const char *path,
int sx,
@@ -177,10 +178,14 @@ static void file_draw_icon(uiBlock *block,
ImBuf *preview_image = filelist_file_getimage(file);
char blend_path[FILE_MAX_LIBEXTRA];
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
+ const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ BLI_assert(asset_params != NULL);
+
UI_but_drag_set_asset(but,
file->name,
BLI_strdup(blend_path),
file->blentype,
+ asset_params->import_type,
icon,
preview_image,
UI_DPI_FAC);
@@ -299,7 +304,8 @@ void file_calc_previews(const bContext *C, ARegion *region)
UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height);
}
-static void file_draw_preview(uiBlock *block,
+static void file_draw_preview(const SpaceFile *sfile,
+ uiBlock *block,
const FileDirEntry *file,
const char *path,
int sx,
@@ -323,6 +329,7 @@ static void file_draw_preview(uiBlock *block,
int ex, ey;
bool show_outline = !is_icon &&
(file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
+ const bool is_offline = (file->attributes & FILE_ATTR_OFFLINE);
BLI_assert(imb != NULL);
@@ -419,14 +426,14 @@ static void file_draw_preview(uiBlock *block,
icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
}
- if (is_link) {
- /* Arrow icon to indicate it is a shortcut, link, or alias. */
+ if (is_link || is_offline) {
+ /* Icon at bottom to indicate it is a shortcut, link, alias, or offline. */
float icon_x, icon_y;
icon_x = xco + (2.0f * UI_DPI_FAC);
icon_y = yco + (2.0f * UI_DPI_FAC);
- const int arrow = ICON_LOOP_FORWARDS;
+ const int arrow = is_link ? ICON_LOOP_FORWARDS : ICON_URL;
if (!is_icon) {
- /* Arrow at very bottom-left if preview style. */
+ /* At very bottom-left if preview style. */
const uchar dark[4] = {0, 0, 0, 255};
const uchar light[4] = {255, 255, 255, 255};
UI_icon_draw_ex(icon_x + 1, icon_y - 1, arrow, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false);
@@ -483,9 +490,19 @@ static void file_draw_preview(uiBlock *block,
/* path is no more static, cannot give it directly to but... */
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);
+ const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ BLI_assert(asset_params != NULL);
+
+ UI_but_drag_set_asset(but,
+ file->name,
+ BLI_strdup(blend_path),
+ file->blentype,
+ asset_params->import_type,
+ icon,
+ imb,
+ scale);
}
}
else {
@@ -924,7 +941,8 @@ void file_draw_list(const bContext *C, ARegion *region)
is_icon = 1;
}
- file_draw_preview(block,
+ file_draw_preview(sfile,
+ block,
file,
path,
sx,
@@ -939,7 +957,8 @@ void file_draw_list(const bContext *C, ARegion *region)
is_link);
}
else {
- file_draw_icon(block,
+ file_draw_icon(sfile,
+ block,
file,
path,
sx,
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index 309b280177c..f1d0197b9ae 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -63,6 +63,7 @@ void FILE_OT_bookmark_move(struct wmOperatorType *ot);
void FILE_OT_reset_recent(wmOperatorType *ot);
void FILE_OT_hidedot(struct wmOperatorType *ot);
void FILE_OT_execute(struct wmOperatorType *ot);
+void FILE_OT_mouse_execute(struct wmOperatorType *ot);
void FILE_OT_cancel(struct wmOperatorType *ot);
void FILE_OT_parent(struct wmOperatorType *ot);
void FILE_OT_directory_new(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 856bd5b1bc3..7c14f4659eb 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -536,6 +536,14 @@ void FILE_OT_select_box(wmOperatorType *ot)
/** \name Select Pick Operator
* \{ */
+static rcti file_select_mval_to_select_rect(const int mval[2])
+{
+ rcti rect;
+ rect.xmin = rect.xmax = mval[0];
+ rect.ymin = rect.ymax = mval[1];
+ return rect;
+}
+
static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
@@ -551,8 +559,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- rect.xmin = rect.xmax = event->mval[0];
- rect.ymin = rect.ymax = event->mval[1];
+ rect = file_select_mval_to_select_rect(event->mval);
if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &region->v2d, rect.xmin, rect.ymin)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
@@ -1711,14 +1718,14 @@ bool file_draw_check_exists(SpaceFile *sfile)
/** \name Execute File Window Operator
* \{ */
-static int file_exec(bContext *C, wmOperator *exec_op)
+/**
+ * Execute the active file, as set in the file select params.
+ */
+static bool file_execute(bContext *C, SpaceFile *sfile)
{
Main *bmain = CTX_data_main(C);
- wmWindowManager *wm = CTX_wm_manager(C);
- SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
- struct FileDirEntry *file = filelist_file(sfile->files, params->active_file);
- char filepath[FILE_MAX];
+ FileDirEntry *file = filelist_file(sfile->files, params->active_file);
if (file && file->redirection_path) {
/* redirection_path is an absolute path that takes precedence
@@ -1737,7 +1744,7 @@ static int file_exec(bContext *C, wmOperator *exec_op)
/* directory change */
if (file && (file->typeflag & FILE_TYPE_DIR)) {
if (!file->relpath) {
- return OPERATOR_CANCELLED;
+ return false;
}
if (FILENAME_IS_PARENT(file->relpath)) {
@@ -1753,22 +1760,7 @@ static int file_exec(bContext *C, wmOperator *exec_op)
/* opening file - sends events now, so things get handled on windowqueue level */
else if (sfile->op) {
wmOperator *op = sfile->op;
-
- /* When used as a macro, for double-click, to prevent closing when double-clicking on item. */
- if (RNA_boolean_get(exec_op->ptr, "need_active")) {
- const int numfiles = filelist_files_ensure(sfile->files);
- int i, active = 0;
-
- for (i = 0; i < numfiles; i++) {
- if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
- active = 1;
- break;
- }
- }
- if (active == 0) {
- return OPERATOR_CANCELLED;
- }
- }
+ char filepath[FILE_MAX];
sfile->op = NULL;
@@ -1788,50 +1780,94 @@ static int file_exec(bContext *C, wmOperator *exec_op)
BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
BLENDER_BOOKMARK_FILE);
fsmenu_write_file(ED_fsmenu_get(), filepath);
- WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC);
+ WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC);
}
- return OPERATOR_FINISHED;
+ return true;
}
-static int file_exec_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int file_exec(bContext *C, wmOperator *UNUSED(op))
{
- ARegion *region = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
- if (!ED_fileselect_layout_is_inside_pt(
- sfile->layout, &region->v2d, event->mval[0], event->mval[1])) {
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ if (!file_execute(C, sfile)) {
+ return OPERATOR_CANCELLED;
}
- return file_exec(C, op);
+ return OPERATOR_FINISHED;
}
void FILE_OT_execute(struct wmOperatorType *ot)
{
- PropertyRNA *prop;
-
/* identifiers */
ot->name = "Execute File Window";
ot->description = "Execute selected file";
ot->idname = "FILE_OT_execute";
/* api callbacks */
- ot->invoke = file_exec_invoke;
ot->exec = file_exec;
/* Important since handler is on window level.
*
* Avoid using #file_operator_poll since this is also used for entering directories
* which is used even when the file manager doesn't have an operator. */
ot->poll = ED_operator_file_active;
+}
- /* properties */
- prop = RNA_def_boolean(ot->srna,
- "need_active",
- 0,
- "Need Active",
- "Only execute if there's an active selected file in the file list");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+/**
+ * \returns false if the mouse doesn't hover a selectable item.
+ */
+static bool file_ensure_hovered_is_active(bContext *C, const wmEvent *event)
+{
+ rcti rect = file_select_mval_to_select_rect(event->mval);
+ if (file_select(C, &rect, FILE_SEL_ADD, false, false) == FILE_SELECT_NOTHING) {
+ return false;
+ }
+
+ return true;
+}
+
+static int file_execute_mouse_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+
+ if (!ED_fileselect_layout_is_inside_pt(
+ sfile->layout, &region->v2d, event->mval[0], event->mval[1])) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ }
+
+ /* Note that this isn't needed practically, because the keymap already activates the hovered item
+ * on mouse-press. This execute operator is called afterwards on the double-click event then.
+ * However relying on this would be fragile and could break with keymap changes, so better to
+ * have this mouse-execute operator that makes sure once more that the hovered file is active. */
+ if (!file_ensure_hovered_is_active(C, event)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (!file_execute(C, sfile)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+/**
+ * Variation of #FILE_OT_execute that accounts for some mouse specific handling. Otherwise calls
+ * the same logic.
+ */
+void FILE_OT_mouse_execute(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Execute File";
+ ot->description =
+ "Perform the current execute action for the file under the cursor (e.g. open the file)";
+ ot->idname = "FILE_OT_mouse_execute";
+
+ /* api callbacks */
+ ot->invoke = file_execute_mouse_invoke;
+ ot->poll = ED_operator_file_active;
+
+ ot->flag = OPTYPE_INTERNAL;
}
/** \} */
@@ -2227,23 +2263,24 @@ void FILE_OT_filepath_drop(wmOperatorType *ot)
* \{ */
/**
- * Create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
+ * Create a new, non-existing folder name, returns true if successful,
+ * false if name couldn't be created.
* The actual name is returned in 'name', 'folder' contains the complete path,
* including the new folder name.
*/
-static int new_folder_path(const char *parent, char *folder, char *name)
+static bool new_folder_path(const char *parent, char folder[FILE_MAX], char name[FILE_MAXFILE])
{
int i = 1;
int len = 0;
BLI_strncpy(name, "New Folder", FILE_MAXFILE);
- BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
+ BLI_join_dirfile(folder, FILE_MAX, parent, name);
/* check whether folder with the name already exists, in this case
* add number to the name. Check length of generated name to avoid
* crazy case of huge number of folders each named 'New Folder (x)' */
while (BLI_exists(folder) && (len < FILE_MAXFILE)) {
len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
- BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
+ BLI_join_dirfile(folder, FILE_MAX, parent, name);
i++;
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 4c9f80bfa64..8ea44e5c3ee 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -1553,7 +1553,8 @@ static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void
static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
{
if (!cache->previews_pool) {
- cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW);
+ cache->previews_pool = BLI_task_pool_create_background(
+ cache, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
cache->previews_done = BLI_thread_queue_init();
IMB_thumb_locks_acquire();
@@ -1601,37 +1602,51 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE);
- 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->preview_icon_id && (entry->attributes & FILE_ATTR_OFFLINE)) {
+ entry->flags |= FILE_ENTRY_INVALID_PREVIEW;
+ return;
+ }
- if (entry->redirection_path) {
- BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
- }
- else {
- BLI_join_dirfile(
- preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
- }
+ if (entry->preview_icon_id) {
+ return;
+ }
- preview->index = index;
- preview->flags = entry->typeflag;
- 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);
+ if (entry->flags & FILE_ENTRY_INVALID_PREVIEW) {
+ return;
+ }
+
+ if (!(entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
+ FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) {
+ return;
+ }
- filelist_cache_preview_ensure_running(cache);
+ FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
+ FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
- FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
- __func__);
- preview_taskdata->preview = preview;
- BLI_task_pool_push(cache->previews_pool,
- filelist_cache_preview_runf,
- preview_taskdata,
- true,
- filelist_cache_preview_freef);
+ if (entry->redirection_path) {
+ BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
+ }
+ else {
+ BLI_join_dirfile(
+ preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
}
+
+ preview->index = index;
+ preview->flags = entry->typeflag;
+ 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);
+
+ FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
+ __func__);
+ preview_taskdata->preview = preview;
+ BLI_task_pool_push(cache->previews_pool,
+ filelist_cache_preview_runf,
+ preview_taskdata,
+ true,
+ filelist_cache_preview_freef);
}
static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
@@ -2360,17 +2375,19 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index)
// printf("Re-queueing previews...\n");
- /* Note we try to preview first images around given index - i.e. assumed visible ones. */
if (cache->flags & FLC_PREVIEWS_ACTIVE) {
- for (i = 0; ((index + i) < end_index) || ((index - i) >= start_index); i++) {
- if ((index - i) >= start_index) {
- const int idx = (cache->block_cursor + (index - start_index) - i) % cache_size;
- filelist_cache_previews_push(filelist, cache->block_entries[idx], index - i);
- }
- if ((index + i) < end_index) {
- const int idx = (cache->block_cursor + (index - start_index) + i) % cache_size;
- filelist_cache_previews_push(filelist, cache->block_entries[idx], index + i);
- }
+ /* Note we try to preview first images around given index - i.e. assumed visible ones. */
+ int block_index = cache->block_cursor + (index - start_index);
+ int offs_max = max_ii(end_index - index, index - start_index);
+ for (i = 0; i <= offs_max; i++) {
+ int offs = i;
+ do {
+ int offs_idx = index + offs;
+ if (start_index <= offs_idx && offs_idx < end_index) {
+ int offs_block_idx = (block_index + offs) % (int)cache_size;
+ filelist_cache_previews_push(filelist, cache->block_entries[offs_block_idx], offs_idx);
+ }
+ } while ((offs = -offs) < 0); /* Switch between negative and positive offset. */
}
}
@@ -3017,8 +3034,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
}
}
- /* XXX TODO: if databrowse F4 or append/link
- * filelist->flags & FLF_HIDE_PARENT has to be set */
+ /* XXX TODO: if data-browse or append/link #FLF_HIDE_PARENT has to be set. */
if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
filelist->filelist.nbr_entries++;
}
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 038b9c11bca..8e3fc36aa71 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -120,6 +120,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
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;
+ asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
}
FileSelectParams *base_params = &asset_params->base_params;
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 993b1d9b69c..12bc0a68ca6 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -467,6 +467,15 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
break;
}
break;
+ case NC_ID: {
+ switch (wmn->action) {
+ case NA_RENAME:
+ /* Force list to update sorting (with a full reset for now). */
+ file_reset_filelist_showing_main_data(area, sfile);
+ break;
+ }
+ break;
+ }
case NC_ASSET: {
switch (wmn->action) {
case NA_SELECTED:
@@ -654,6 +663,7 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_highlight);
WM_operatortype_append(FILE_OT_sort_column_ui_context);
WM_operatortype_append(FILE_OT_execute);
+ WM_operatortype_append(FILE_OT_mouse_execute);
WM_operatortype_append(FILE_OT_cancel);
WM_operatortype_append(FILE_OT_parent);
WM_operatortype_append(FILE_OT_previous);
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 31f606e515d..a2e812a4ed1 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -578,6 +578,19 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static char *graphkeys_paste_description(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(op),
+ PointerRNA *ptr)
+{
+ /* Custom description if the 'flipped' option is used. */
+ if (RNA_boolean_get(ptr, "flipped")) {
+ return BLI_strdup("Paste keyframes from mirrored bones if they exist");
+ }
+
+ /* Use the default description in the other cases. */
+ return NULL;
+}
+
void GRAPH_OT_paste(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -592,6 +605,7 @@ void GRAPH_OT_paste(wmOperatorType *ot)
/* API callbacks */
// ot->invoke = WM_operator_props_popup; /* better wait for graph redo panel */
+ ot->get_description = graphkeys_paste_description;
ot->exec = graphkeys_paste_exec;
ot->poll = graphop_editable_keyframes_poll;
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index 4ab4ef518fb..068ee177d59 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -512,16 +512,19 @@ static rctf initialize_box_select_coords(const bAnimContext *ac, const rctf *rec
return rectf;
}
-static ListBase initialize_box_select_anim_data(const SpaceGraph *sipo, bAnimContext *ac)
+static int initialize_animdata_selection_filter(const SpaceGraph *sipo)
{
- ListBase anim_data = {NULL, NULL};
-
int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
if (sipo->flag & SIPO_SELCUVERTSONLY) {
filter |= ANIMFILTER_FOREDIT | ANIMFILTER_SELEDIT;
}
- ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+ return filter;
+}
+static ListBase initialize_box_select_anim_data(const int filter, bAnimContext *ac)
+{
+ ListBase anim_data = {NULL, NULL};
+ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
return anim_data;
}
@@ -573,8 +576,11 @@ static void initialize_box_select_key_editing_data(const SpaceGraph *sipo,
* which means that they may be inadvertently moved as well. However, incl_handles overrides
* this, and allow handles to be considered independently too.
* Also, for convenience, handles should get same status as keyframe (if it was within bounds).
+ *
+ * This function returns true if there was any change in the selection of a key (selecting or
+ * deselecting any key returns true, otherwise it returns false).
*/
-static void box_select_graphkeys(bAnimContext *ac,
+static bool box_select_graphkeys(bAnimContext *ac,
const rctf *rectf_view,
short mode,
short selectmode,
@@ -583,7 +589,8 @@ static void box_select_graphkeys(bAnimContext *ac,
{
const rctf rectf = initialize_box_select_coords(ac, rectf_view);
SpaceGraph *sipo = (SpaceGraph *)ac->sl;
- ListBase anim_data = initialize_box_select_anim_data(sipo, ac);
+ const int filter = initialize_animdata_selection_filter(sipo);
+ ListBase anim_data = initialize_box_select_anim_data(filter, ac);
rctf scaled_rectf;
KeyframeEditData ked;
int mapping_flag;
@@ -597,6 +604,9 @@ static void box_select_graphkeys(bAnimContext *ac,
/* Try selecting the keyframes. */
bAnimListElem *ale = NULL;
+ /* This variable will be set to true if any key is selected or deselected. */
+ bool any_key_selection_changed = false;
+
/* First loop over data, doing box select. try selecting keys only. */
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
@@ -634,7 +644,7 @@ static void box_select_graphkeys(bAnimContext *ac,
if (ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, ok_cb, NULL)) {
/* select keyframes that are in the appropriate places */
ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL);
-
+ any_key_selection_changed = true;
/* Only change selection of channel when the visibility of keyframes
* doesn't depend on this. */
if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
@@ -653,6 +663,125 @@ static void box_select_graphkeys(bAnimContext *ac,
/* Cleanup. */
ANIM_animdata_freelist(&anim_data);
+
+ return any_key_selection_changed;
+}
+
+/* This function is used to set all the keyframes of a given curve as selectable
+ * by the "select_cb" function inside of "box_select_graphcurves".
+ */
+static short ok_bezier_always_ok(KeyframeEditData *UNUSED(ked), BezTriple *UNUSED(bezt))
+{
+ return KEYFRAME_OK_KEY | KEYFRAME_OK_H1 | KEYFRAME_OK_H2;
+}
+
+/* Checks whether the given rectangle intersects the given fcurve's calculated curve (i.e. not
+ * only keyframes, but also all the interpolated values). This is done by sampling the curve at
+ * different points between the xmin and the xmax of the rectangle.
+ */
+static bool rectf_curve_intersection(
+ const float offset, const float unit_scale, const rctf *rectf, AnimData *adt, FCurve *fcu)
+{
+ /* 30 sampling points. This worked well in tests. */
+ const float num_steps = 30.0f;
+ const float step = (rectf->xmax - rectf->xmin) / num_steps;
+
+ /* Remap the range at which to evaluate the fcurves. This enables us to avoid remapping
+ * the keys themselves. */
+ const float mapped_max = BKE_nla_tweakedit_remap(adt, rectf->xmax, NLATIME_CONVERT_UNMAP);
+ const float mapped_min = BKE_nla_tweakedit_remap(adt, rectf->xmin, NLATIME_CONVERT_UNMAP);
+ const float eval_step = (mapped_max - mapped_min) / num_steps;
+
+ float x = rectf->xmin;
+ float eval_x = mapped_min;
+ /* Sample points on the given fcurve in the interval defined by the
+ * mapped_min and mapped_max of the selected rectangle.
+ * For each point, check if it is inside of the selection box. If it is, then select
+ * all the keyframes of the curve, the curve, and stop the loop.
+ */
+ while (x < rectf->xmax) {
+ const float fcurve_y = (evaluate_fcurve(fcu, eval_x) + offset) * unit_scale;
+ /* Since rectf->xmin <= x < rectf->xmax is always true, there is no need to keep comparing the
+ * X-coordinate to the rectangle in every iteration. Therefore we do the comparisons manually
+ * instead of using BLI_rctf_isect_pt_v(rectf, current_point).
+ */
+ if (rectf->ymin <= fcurve_y && fcurve_y <= rectf->ymax) {
+ return true;
+ }
+ x += step;
+ eval_x += eval_step;
+ }
+ return false;
+}
+
+/* Perform a box selection of the curves themselves. This means this function tries
+ * to select a curve by sampling it at various points instead of trying to select the
+ * keyframes directly.
+ * The selection actions done to a curve are actually done on all the keyframes of the curve.
+ * Note: This function is only called if no keyframe is in the selection area.
+ */
+static void box_select_graphcurves(bAnimContext *ac,
+ const rctf *rectf_view,
+ const short mode,
+ const short selectmode,
+ const bool incl_handles,
+ void *data)
+{
+ const SpaceGraph *sipo = (SpaceGraph *)ac->sl;
+ const int filter = initialize_animdata_selection_filter(sipo);
+ ListBase anim_data = initialize_box_select_anim_data(filter, ac);
+ rctf scaled_rectf;
+ KeyframeEditData ked;
+ int mapping_flag;
+ initialize_box_select_key_editing_data(
+ sipo, incl_handles, mode, ac, data, &scaled_rectf, &ked, &mapping_flag);
+
+ FCurve *last_selected_curve = NULL;
+
+ /* Go through all the curves and try selecting them. This function is only called
+ * if no keyframe is in the selection area, so we only have to check if the curve
+ * intersects the area in order to check if the selection/deselection must happen.
+ */
+
+ LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
+ AnimData *adt = ANIM_nla_mapping_get(ac, ale);
+ FCurve *fcu = (FCurve *)ale->key_data;
+ float offset;
+ const float unit_scale = ANIM_unit_mapping_get_factor(
+ ac->scene, ale->id, fcu, mapping_flag, &offset);
+
+ const rctf rectf = initialize_box_select_coords(ac, rectf_view);
+
+ /* scaled_rectf is declared at the top of the block because it is required by the
+ * initialize_box_select_key_editing_data function (which does
+ * data_xxx->rectf_scaled = scaled_rectf). The below assignment therefore modifies the
+ * data we use to iterate over the curves (ked).
+ */
+ scaled_rectf.xmin = rectf.xmin;
+ scaled_rectf.xmax = rectf.xmax;
+ scaled_rectf.ymin = rectf.ymin / unit_scale - offset;
+ scaled_rectf.ymax = rectf.ymax / unit_scale - offset;
+
+ const KeyframeEditFunc select_cb = ANIM_editkeyframes_select(selectmode);
+ if (rectf_curve_intersection(offset, unit_scale, &rectf, adt, fcu)) {
+ if ((selectmode & SELECT_ADD) || (selectmode & SELECT_REPLACE)) {
+ fcu->flag |= FCURVE_SELECTED;
+ last_selected_curve = fcu;
+ }
+ else {
+ fcu->flag &= ~FCURVE_SELECTED;
+ }
+ ANIM_fcurve_keyframes_loop(&ked, fcu, ok_bezier_always_ok, select_cb, NULL);
+ }
+ }
+
+ /* Make sure that one of the selected curves is active in the end. */
+ if (last_selected_curve != NULL) {
+ ANIM_set_active_channel(
+ ac, ac->data, ac->datatype, filter, last_selected_curve, ANIMTYPE_FCURVE);
+ }
+
+ ANIM_animdata_freelist(&anim_data);
}
/* ------------------- */
@@ -726,7 +855,12 @@ static int graphkeys_box_select_exec(bContext *C, wmOperator *op)
BLI_rctf_rcti_copy(&rect_fl, &rect);
/* Apply box_select action. */
- box_select_graphkeys(&ac, &rect_fl, mode, selectmode, incl_handles, NULL);
+ const bool any_key_selection_changed = box_select_graphkeys(
+ &ac, &rect_fl, mode, selectmode, incl_handles, NULL);
+ const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection");
+ if (use_curve_selection && !any_key_selection_changed) {
+ box_select_graphcurves(&ac, &rect_fl, mode, selectmode, incl_handles, NULL);
+ }
/* Send notifier that keyframe selection has changed. */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
@@ -767,6 +901,14 @@ void GRAPH_OT_select_box(wmOperatorType *ot)
ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(
+ ot->srna,
+ "use_curve_selection",
+ 1,
+ "Select Curves",
+ "Allow selecting all the keyframes of a curve by selecting the calculated fcurve");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
WM_operator_properties_gesture_box(ot);
WM_operator_properties_select_operation_simple(ot);
}
@@ -815,7 +957,13 @@ static int graphkeys_lassoselect_exec(bContext *C, wmOperator *op)
BLI_rctf_rcti_copy(&rect_fl, &rect);
/* Apply box_select action. */
- box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso);
+ const bool any_key_selection_changed = box_select_graphkeys(
+ &ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso);
+ const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection");
+ if (use_curve_selection && !any_key_selection_changed) {
+ box_select_graphcurves(
+ &ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso);
+ }
MEM_freeN((void *)data_lasso.mcoords);
@@ -845,6 +993,13 @@ void GRAPH_OT_select_lasso(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_gesture_lasso(ot);
WM_operator_properties_select_operation_simple(ot);
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna,
+ "use_curve_selection",
+ 1,
+ "Select Curves",
+ "Allow selecting all the keyframes of a curve by selecting the curve itself");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ------------------- */
@@ -894,7 +1049,12 @@ static int graph_circle_select_exec(bContext *C, wmOperator *op)
}
/* Apply box_select action. */
- box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data);
+ const bool any_key_selection_changed = box_select_graphkeys(
+ &ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data);
+ const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection");
+ if (use_curve_selection && !any_key_selection_changed) {
+ box_select_graphcurves(&ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data);
+ }
/* Send notifier that keyframe selection has changed. */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
@@ -920,6 +1080,13 @@ void GRAPH_OT_select_circle(wmOperatorType *ot)
/* properties */
WM_operator_properties_gesture_circle(ot);
WM_operator_properties_select_operation_simple(ot);
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna,
+ "use_curve_selection",
+ 1,
+ "Select Curves",
+ "Allow selecting all the keyframes of a curve by selecting the curve itself");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ******************** Column Select Operator **************************** */
@@ -1638,7 +1805,6 @@ static int graphkeys_mselect_column(bAnimContext *ac,
KeyframeEditFunc select_cb, ok_cb;
KeyframeEditData ked;
tNearestVertInfo *nvi;
- float selx = (float)ac->scene->r.cfra;
/* find the beztriple that we're selecting, and the handle that was clicked on */
nvi = find_nearest_fcurve_vert(ac, mval);
@@ -1650,7 +1816,7 @@ static int graphkeys_mselect_column(bAnimContext *ac,
/* get frame number on which elements should be selected */
/* TODO: should we restrict to integer frames only? */
- selx = nvi->frame;
+ const float selx = nvi->frame;
if (select_mode != SELECT_REPLACE) {
/* Doesn't need to deselect anything -> Pass. */
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 6fb64de7e85..d909bfd1864 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -1218,11 +1218,12 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i
const int len = MAX_IMAGE_INFO_LEN;
int ofs = 0;
- ofs += BLI_snprintf(str + ofs, len - ofs, TIP_("%d x %d, "), ibuf->x, ibuf->y);
+ ofs += BLI_snprintf_rlen(str + ofs, len - ofs, TIP_("%d x %d, "), ibuf->x, ibuf->y);
if (ibuf->rect_float) {
if (ibuf->channels != 4) {
- ofs += BLI_snprintf(str + ofs, len - ofs, TIP_("%d float channel(s)"), ibuf->channels);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, len - ofs, TIP_("%d float channel(s)"), ibuf->channels);
}
else if (ibuf->planes == R_IMF_PLANES_RGBA) {
ofs += BLI_strncpy_rlen(str + ofs, TIP_(" RGBA float"), len - ofs);
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 2be6d31369c..dc693b25107 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -83,7 +83,6 @@ static void draw_render_info(
const bContext *C, Scene *scene, Image *ima, ARegion *region, float zoomx, float zoomy)
{
Render *re = RE_GetSceneRender(scene);
- RenderData *rd = RE_engine_get_render_data(re);
Scene *stats_scene = ED_render_job_get_scene(C);
if (stats_scene == NULL) {
stats_scene = CTX_data_scene(C);
@@ -112,6 +111,7 @@ static void draw_render_info(
GPU_matrix_translate_2f(x, y);
GPU_matrix_scale_2f(zoomx, zoomy);
+ RenderData *rd = RE_engine_get_render_data(re);
if (rd->mode & R_BORDER) {
/* TODO: round or floor instead of casting to int */
GPU_matrix_translate_2f((int)(-rd->border.xmin * rd->xsch * rd->size * 0.01f),
diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h
index 0044c6072a4..d302f099772 100644
--- a/source/blender/editors/space_image/image_intern.h
+++ b/source/blender/editors/space_image/image_intern.h
@@ -50,6 +50,7 @@ void IMAGE_OT_view_all(struct wmOperatorType *ot);
void IMAGE_OT_view_pan(struct wmOperatorType *ot);
void IMAGE_OT_view_selected(struct wmOperatorType *ot);
void IMAGE_OT_view_center_cursor(struct wmOperatorType *ot);
+void IMAGE_OT_view_cursor_center(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 72405a51aca..0c34c0cc756 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -244,6 +244,70 @@ static bool image_not_packed_poll(bContext *C)
return (ima && BLI_listbase_is_empty(&ima->packedfiles));
}
+static void image_view_all(struct SpaceImage *sima, struct ARegion *region, struct wmOperator *op)
+{
+ float aspx, aspy, zoomx, zoomy, w, h;
+ int width, height;
+ const bool fit_view = RNA_boolean_get(op->ptr, "fit_view");
+
+ ED_space_image_get_size(sima, &width, &height);
+ ED_space_image_get_aspect(sima, &aspx, &aspy);
+
+ w = width * aspx;
+ h = height * aspy;
+
+ float xof = 0.0f, yof = 0.0f;
+ if ((sima->image == NULL) || (sima->image->source == IMA_SRC_TILED)) {
+ /* Extend the shown area to cover all UDIM tiles. */
+ int x_tiles, y_tiles;
+ if (sima->image == NULL) {
+ x_tiles = sima->tile_grid_shape[0];
+ y_tiles = sima->tile_grid_shape[1];
+ }
+ else {
+ x_tiles = y_tiles = 1;
+ LISTBASE_FOREACH (ImageTile *, tile, &sima->image->tiles) {
+ int tile_x = (tile->tile_number - 1001) % 10;
+ int tile_y = (tile->tile_number - 1001) / 10;
+ x_tiles = max_ii(x_tiles, tile_x + 1);
+ y_tiles = max_ii(y_tiles, tile_y + 1);
+ }
+ }
+ xof = 0.5f * (x_tiles - 1.0f) * w;
+ yof = 0.5f * (y_tiles - 1.0f) * h;
+ w *= x_tiles;
+ h *= y_tiles;
+ }
+
+ /* check if the image will fit in the image with (zoom == 1) */
+ width = BLI_rcti_size_x(&region->winrct) + 1;
+ height = BLI_rcti_size_y(&region->winrct) + 1;
+
+ if (fit_view) {
+ const int margin = 5; /* margin from border */
+
+ zoomx = (float)width / (w + 2 * margin);
+ zoomy = (float)height / (h + 2 * margin);
+
+ sima_zoom_set(sima, region, min_ff(zoomx, zoomy), NULL, false);
+ }
+ else {
+ if ((w >= width || h >= height) && (width > 0 && height > 0)) {
+ zoomx = (float)width / w;
+ zoomy = (float)height / h;
+
+ /* find the zoom value that will fit the image in the image space */
+ sima_zoom_set(sima, region, 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)), NULL, false);
+ }
+ else {
+ sima_zoom_set(sima, region, 1.0f, NULL, false);
+ }
+ }
+
+ sima->xof = xof;
+ sima->yof = yof;
+}
+
bool space_image_main_region_poll(bContext *C)
{
SpaceImage *sima = CTX_wm_space_image(C);
@@ -736,70 +800,12 @@ static int image_view_all_exec(bContext *C, wmOperator *op)
{
SpaceImage *sima;
ARegion *region;
- float aspx, aspy, zoomx, zoomy, w, h;
- int width, height;
- const bool fit_view = RNA_boolean_get(op->ptr, "fit_view");
/* retrieve state */
sima = CTX_wm_space_image(C);
region = CTX_wm_region(C);
- ED_space_image_get_size(sima, &width, &height);
- ED_space_image_get_aspect(sima, &aspx, &aspy);
-
- w = width * aspx;
- h = height * aspy;
-
- float xof = 0.0f, yof = 0.0f;
- if ((sima->image == NULL) || (sima->image->source == IMA_SRC_TILED)) {
- /* Extend the shown area to cover all UDIM tiles. */
- int x_tiles, y_tiles;
- if (sima->image == NULL) {
- x_tiles = sima->tile_grid_shape[0];
- y_tiles = sima->tile_grid_shape[1];
- }
- else {
- x_tiles = y_tiles = 1;
- LISTBASE_FOREACH (ImageTile *, tile, &sima->image->tiles) {
- int tile_x = (tile->tile_number - 1001) % 10;
- int tile_y = (tile->tile_number - 1001) / 10;
- x_tiles = max_ii(x_tiles, tile_x + 1);
- y_tiles = max_ii(y_tiles, tile_y + 1);
- }
- }
- xof = 0.5f * (x_tiles - 1.0f) * w;
- yof = 0.5f * (y_tiles - 1.0f) * h;
- w *= x_tiles;
- h *= y_tiles;
- }
-
- /* check if the image will fit in the image with (zoom == 1) */
- width = BLI_rcti_size_x(&region->winrct) + 1;
- height = BLI_rcti_size_y(&region->winrct) + 1;
-
- if (fit_view) {
- const int margin = 5; /* margin from border */
-
- zoomx = (float)width / (w + 2 * margin);
- zoomy = (float)height / (h + 2 * margin);
-
- sima_zoom_set(sima, region, min_ff(zoomx, zoomy), NULL, false);
- }
- else {
- if ((w >= width || h >= height) && (width > 0 && height > 0)) {
- zoomx = (float)width / w;
- zoomy = (float)height / h;
-
- /* find the zoom value that will fit the image in the image space */
- sima_zoom_set(sima, region, 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)), NULL, false);
- }
- else {
- sima_zoom_set(sima, region, 1.0f, NULL, false);
- }
- }
-
- sima->xof = xof;
- sima->yof = yof;
+ image_view_all(sima, region, op);
ED_region_tag_redraw(region);
@@ -830,6 +836,49 @@ void IMAGE_OT_view_all(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Cursor To Center View Operator
+ * \{ */
+
+static int view_cursor_center_exec(bContext *C, wmOperator *op)
+{
+ SpaceImage *sima;
+ ARegion *region;
+
+ sima = CTX_wm_space_image(C);
+ region = CTX_wm_region(C);
+
+ image_view_all(sima, region, op);
+
+ sima->cursor[0] = 0.5f;
+ sima->cursor[1] = 0.5f;
+
+ /* Needed for updating the cursor. */
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void IMAGE_OT_view_cursor_center(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Cursor To Center View";
+ ot->description = "Set 2D Cursor To Center View location";
+ ot->idname = "IMAGE_OT_view_cursor_center";
+
+ /* api callbacks */
+ ot->exec = view_cursor_center_exec;
+ ot->poll = ED_space_image_cursor_poll;
+
+ /* properties */
+ prop = RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Center View To Cursor Operator
* \{ */
@@ -1262,7 +1311,6 @@ static int image_open_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
ImageUser *iuser = NULL;
- ImageOpenData *iod = op->customdata;
Image *ima = NULL;
int frame_seq_len = 0;
int frame_ofs = 1;
@@ -1296,7 +1344,7 @@ static int image_open_exec(bContext *C, wmOperator *op)
}
/* hook into UI */
- iod = op->customdata;
+ ImageOpenData *iod = op->customdata;
if (iod->pprop.prop) {
/* when creating new ID blocks, use is already 1, but RNA
@@ -1435,12 +1483,11 @@ static void image_open_draw(bContext *UNUSED(C), wmOperator *op)
uiLayout *layout = op->layout;
ImageOpenData *iod = op->customdata;
ImageFormatData *imf = &iod->im_format;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
/* main draw call */
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* image template */
RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
@@ -1955,7 +2002,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
ImageSaveData *isd = op->customdata;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview");
/* image template */
@@ -1963,9 +2010,8 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
uiTemplateImageSettings(layout, &imf_ptr, false);
/* main draw call */
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* multiview template */
if (is_multiview) {
@@ -2567,33 +2613,30 @@ static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *col;
uiLayout *layout = op->layout;
- PointerRNA ptr;
#if 0
Scene *scene = CTX_data_scene(C);
const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
#endif
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
/* copy of WM_operator_props_dialog_popup() layout */
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
col = uiLayoutColumn(layout, false);
- uiItemR(col, &ptr, "name", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "width", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "height", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "color", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "alpha", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "generated_type", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "float", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "tiled", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "name", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "color", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "alpha", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "generated_type", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "float", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "tiled", 0, NULL, ICON_NONE);
#if 0
if (is_multiview) {
uiItemL(col[0], "", ICON_NONE);
- uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE);
+ uiItemR(col[1], op->ptr, "use_stereo_3d", 0, NULL, ICON_NONE);
}
#endif
}
@@ -3943,21 +3986,18 @@ static void tile_add_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *col;
uiLayout *layout = op->layout;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
col = uiLayoutColumn(layout, false);
- uiItemR(col, &ptr, "number", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "count", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "label", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "fill", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "number", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "count", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "label", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "fill", 0, NULL, ICON_NONE);
- if (RNA_boolean_get(&ptr, "fill")) {
- draw_fill_tile(&ptr, layout);
+ if (RNA_boolean_get(op->ptr, "fill")) {
+ draw_fill_tile(op->ptr, layout);
}
}
@@ -4077,10 +4117,7 @@ static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e
static void tile_fill_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- draw_fill_tile(&ptr, op->layout);
+ draw_fill_tile(op->ptr, op->layout);
}
void IMAGE_OT_tile_fill(wmOperatorType *ot)
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index c51d2f25efd..5a03b4f6ef0 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -200,6 +200,7 @@ static void image_operatortypes(void)
WM_operatortype_append(IMAGE_OT_view_pan);
WM_operatortype_append(IMAGE_OT_view_selected);
WM_operatortype_append(IMAGE_OT_view_center_cursor);
+ WM_operatortype_append(IMAGE_OT_view_cursor_center);
WM_operatortype_append(IMAGE_OT_view_zoom);
WM_operatortype_append(IMAGE_OT_view_zoom_in);
WM_operatortype_append(IMAGE_OT_view_zoom_out);
diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c
index be3b60d581b..9524c2a1d8a 100644
--- a/source/blender/editors/space_info/info_draw.c
+++ b/source/blender/editors/space_info/info_draw.c
@@ -134,14 +134,12 @@ static void report_textview_end(TextViewContext *UNUSED(tvc))
static int report_textview_step(TextViewContext *tvc)
{
/* simple case, but no newline support */
- const Report *report = tvc->iter;
-
if (tvc->iter_char_begin <= 0) {
tvc->iter = (void *)((Link *)tvc->iter)->prev;
if (tvc->iter && report_textview_skip__internal(tvc)) {
tvc->iter_tmp++;
- report = tvc->iter;
+ const Report *report = tvc->iter;
tvc->iter_char_end = report->len; /* reset start */
report_textview_init__internal(tvc);
diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c
index 0583628be43..aaf9852e212 100644
--- a/source/blender/editors/space_info/info_ops.c
+++ b/source/blender/editors/space_info/info_ops.c
@@ -56,7 +56,9 @@
#include "info_intern.h"
-/********************* pack blend file libraries operator *********************/
+/* -------------------------------------------------------------------- */
+/** \name Pack Blend File Libraries Operator
+ * \{ */
static int pack_libraries_exec(bContext *C, wmOperator *op)
{
@@ -70,9 +72,11 @@ static int pack_libraries_exec(bContext *C, wmOperator *op)
void FILE_OT_pack_libraries(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Pack Blender Libraries";
+ ot->name = "Pack Linked Libraries";
ot->idname = "FILE_OT_pack_libraries";
- ot->description = "Pack all used Blender library files into the current .blend";
+ ot->description =
+ "Store all data-blocks linked from other .blend files in the current .blend file. "
+ "Library references are preserved so the linked data-blocks can be unpacked again";
/* api callbacks */
ot->exec = pack_libraries_exec;
@@ -90,18 +94,24 @@ static int unpack_libraries_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack Blend File Libraries Operator
+ * \{ */
+
static int unpack_libraries_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
return WM_operator_confirm_message(
- C, op, "Unpack Blender Libraries - creates directories, all new paths should work");
+ C, op, "Unpack Linked Libraries - creates directories, all new paths should work");
}
void FILE_OT_unpack_libraries(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Unpack Blender Libraries";
+ ot->name = "Unpack Linked Libraries";
ot->idname = "FILE_OT_unpack_libraries";
- ot->description = "Unpack all used Blender library files from this .blend file";
+ ot->description = "Restore all packed linked data-blocks to their original locations";
/* api callbacks */
ot->invoke = unpack_libraries_invoke;
@@ -111,7 +121,11 @@ void FILE_OT_unpack_libraries(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* toggle auto-pack operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Toggle Auto-Pack Operator
+ * \{ */
static int autopack_toggle_exec(bContext *C, wmOperator *op)
{
@@ -131,7 +145,7 @@ static int autopack_toggle_exec(bContext *C, wmOperator *op)
void FILE_OT_autopack_toggle(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Automatically Pack Into .blend";
+ ot->name = "Automatically Pack Resources";
ot->idname = "FILE_OT_autopack_toggle";
ot->description = "Automatically pack all external files into the .blend file";
@@ -142,7 +156,11 @@ void FILE_OT_autopack_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* pack all operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pack All Operator
+ * \{ */
static int pack_all_exec(bContext *C, wmOperator *op)
{
@@ -176,9 +194,9 @@ static int pack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev
void FILE_OT_pack_all(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Pack All Into .blend";
+ ot->name = "Pack Resources";
ot->idname = "FILE_OT_pack_all";
- ot->description = "Pack all used external files into the .blend";
+ ot->description = "Pack all used external files into this .blend";
/* api callbacks */
ot->exec = pack_all_exec;
@@ -188,7 +206,11 @@ void FILE_OT_pack_all(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* unpack all operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack All Operator
+ * \{ */
static const EnumPropertyItem unpack_all_method_items[] = {
{PF_USE_LOCAL, "USE_LOCAL", 0, "Use files in current directory (create when necessary)", ""},
@@ -263,7 +285,7 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
void FILE_OT_unpack_all(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Unpack All Into Files";
+ ot->name = "Unpack Resources";
ot->idname = "FILE_OT_unpack_all";
ot->description = "Unpack all files packed into this .blend to external ones";
@@ -279,7 +301,11 @@ void FILE_OT_unpack_all(wmOperatorType *ot)
ot->srna, "method", unpack_all_method_items, PF_USE_LOCAL, "Method", "How to unpack");
}
-/********************* unpack single item operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack Single Item Operator
+ * \{ */
static const EnumPropertyItem unpack_item_method_items[] = {
{PF_USE_LOCAL, "USE_LOCAL", 0, "Use file from current directory (create when necessary)", ""},
@@ -373,7 +399,11 @@ void FILE_OT_unpack_item(wmOperatorType *ot)
INT_MAX);
}
-/********************* make paths relative operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Make Paths Relative Operator
+ * \{ */
static int make_paths_relative_exec(bContext *C, wmOperator *op)
{
@@ -395,7 +425,7 @@ static int make_paths_relative_exec(bContext *C, wmOperator *op)
void FILE_OT_make_paths_relative(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Make All Paths Relative";
+ ot->name = "Make Paths Relative";
ot->idname = "FILE_OT_make_paths_relative";
ot->description = "Make all paths to external files relative to current .blend";
@@ -406,7 +436,11 @@ void FILE_OT_make_paths_relative(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* make paths absolute operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Make Paths Absolute Operator
+ * \{ */
static int make_paths_absolute_exec(bContext *C, wmOperator *op)
{
@@ -428,7 +462,7 @@ static int make_paths_absolute_exec(bContext *C, wmOperator *op)
void FILE_OT_make_paths_absolute(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Make All Paths Absolute";
+ ot->name = "Make Paths Absolute";
ot->idname = "FILE_OT_make_paths_absolute";
ot->description = "Make all paths to external files absolute";
@@ -439,7 +473,11 @@ void FILE_OT_make_paths_absolute(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* report missing files operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Report Missing Files Operator
+ * \{ */
static int report_missing_files_exec(bContext *C, wmOperator *op)
{
@@ -465,7 +503,11 @@ void FILE_OT_report_missing_files(wmOperatorType *ot)
ot->flag = 0; /* only reports so no need to undo/register */
}
-/********************* find missing files operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Find Missing Files Operator
+ * \{ */
static int find_missing_files_exec(bContext *C, wmOperator *op)
{
@@ -516,7 +558,11 @@ void FILE_OT_find_missing_files(wmOperatorType *ot)
FILE_SORT_DEFAULT);
}
-/********************* report box operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Report Box Operator
+ * \{ */
/* Hard to decide whether to keep this as an operator,
* or turn it into a hardcoded ui control feature,
@@ -621,3 +667,5 @@ void INFO_OT_reports_display_update(wmOperatorType *ot)
}
/* report operators */
+
+/** \} */
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index ffac5c982d6..ebdf32a1cdb 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -505,14 +505,14 @@ static void get_stats_string(
LayerCollection *layer_collection = view_layer->active_collection;
if (object_mode == OB_MODE_OBJECT) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- "%s | ",
- BKE_collection_ui_name_get(layer_collection->collection));
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ "%s | ",
+ BKE_collection_ui_name_get(layer_collection->collection));
}
if (ob) {
- *ofs += BLI_snprintf(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2);
+ *ofs += BLI_snprintf_rlen(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2);
}
if (obedit) {
@@ -521,72 +521,72 @@ static void get_stats_string(
}
if (obedit->type == OB_MESH) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"),
- stats_fmt->totvertsel,
- stats_fmt->totvert,
- stats_fmt->totedgesel,
- stats_fmt->totedge,
- stats_fmt->totfacesel,
- stats_fmt->totface,
- stats_fmt->tottri);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"),
+ stats_fmt->totvertsel,
+ stats_fmt->totvert,
+ stats_fmt->totedgesel,
+ stats_fmt->totedge,
+ stats_fmt->totfacesel,
+ stats_fmt->totface,
+ stats_fmt->tottri);
}
else if (obedit->type == OB_ARMATURE) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Joints:%s/%s | Bones:%s/%s"),
- stats_fmt->totvertsel,
- stats_fmt->totvert,
- stats_fmt->totbonesel,
- stats_fmt->totbone);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Joints:%s/%s | Bones:%s/%s"),
+ stats_fmt->totvertsel,
+ stats_fmt->totvert,
+ stats_fmt->totbonesel,
+ stats_fmt->totbone);
}
else {
- *ofs += BLI_snprintf(
+ *ofs += BLI_snprintf_rlen(
info + *ofs, len - *ofs, TIP_("Verts:%s/%s"), stats_fmt->totvertsel, stats_fmt->totvert);
}
}
else if (ob && (object_mode & OB_MODE_POSE)) {
- *ofs += BLI_snprintf(
+ *ofs += BLI_snprintf_rlen(
info + *ofs, len - *ofs, TIP_("Bones:%s/%s"), stats_fmt->totbonesel, stats_fmt->totbone);
}
else if ((ob) && (ob->type == OB_GPENCIL)) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"),
- stats_fmt->totgplayer,
- stats_fmt->totgpframe,
- stats_fmt->totgpstroke,
- stats_fmt->totgppoint);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"),
+ stats_fmt->totgplayer,
+ stats_fmt->totgpframe,
+ stats_fmt->totgpstroke,
+ stats_fmt->totgppoint);
}
else if (ob && (object_mode & OB_MODE_SCULPT)) {
if (stats_is_object_dynamic_topology_sculpt(ob)) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Verts:%s | Tris:%s"),
- stats_fmt->totvert,
- stats_fmt->tottri);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s | Tris:%s"),
+ stats_fmt->totvert,
+ stats_fmt->tottri);
}
else {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Verts:%s/%s | Faces:%s/%s"),
- stats_fmt->totvertsculpt,
- stats_fmt->totvert,
- stats_fmt->totfacesculpt,
- stats_fmt->totface);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s/%s | Faces:%s/%s"),
+ stats_fmt->totvertsculpt,
+ stats_fmt->totvert,
+ stats_fmt->totfacesculpt,
+ stats_fmt->totface);
}
}
else {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Verts:%s | Faces:%s | Tris:%s"),
- stats_fmt->totvert,
- stats_fmt->totface,
- stats_fmt->tottri);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s | Faces:%s | Tris:%s"),
+ stats_fmt->totvert,
+ stats_fmt->totface,
+ stats_fmt->tottri);
}
- *ofs += BLI_snprintf(
+ *ofs += BLI_snprintf_rlen(
info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj);
}
@@ -613,11 +613,11 @@ static const char *info_statusbar_string(Main *bmain,
/* Memory status. */
if (statusbar_flag & STATUSBAR_SHOW_MEMORY) {
if (info[0]) {
- ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | ");
}
uintptr_t mem_in_use = MEM_get_memory_in_use();
BLI_str_format_byte_unit(formatted_mem, mem_in_use, false);
- ofs += BLI_snprintf(info + ofs, len, TIP_("Memory: %s"), formatted_mem);
+ ofs += BLI_snprintf_rlen(info + ofs, len, TIP_("Memory: %s"), formatted_mem);
}
/* GPU VRAM status. */
@@ -627,27 +627,27 @@ static const char *info_statusbar_string(Main *bmain,
float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f;
float gpu_free_gb = gpu_free_mem_kb / 1048576.0f;
if (info[0]) {
- ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | ");
}
if (gpu_free_mem_kb && gpu_tot_mem_kb) {
- ofs += BLI_snprintf(info + ofs,
- len - ofs,
- TIP_("VRAM: %.1f/%.1f GiB"),
- gpu_total_gb - gpu_free_gb,
- gpu_total_gb);
+ ofs += BLI_snprintf_rlen(info + ofs,
+ len - ofs,
+ TIP_("VRAM: %.1f/%.1f GiB"),
+ gpu_total_gb - gpu_free_gb,
+ gpu_total_gb);
}
else {
/* Can only show amount of GPU VRAM available. */
- ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb);
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb);
}
}
/* Blender version. */
if (statusbar_flag & STATUSBAR_SHOW_VERSION) {
if (info[0]) {
- ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | ");
}
- ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string());
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string());
}
return info;
@@ -693,11 +693,12 @@ void ED_info_draw_stats(
Object *ob = OBACT(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
eObjectMode object_mode = ob ? ob->mode : OB_MODE_OBJECT;
- const int font_id = BLF_default();
+ const int font_id = BLF_set_default();
UI_FontThemeColor(font_id, TH_TEXT_HI);
BLF_enable(font_id, BLF_SHADOW);
- BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f});
+ const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ BLF_shadow(font_id, 5, shadow_color);
BLF_shadow_offset(font_id, 1, -1);
/* Translated labels for each stat row. */
diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c
index dfc0abee704..e56bb44b1e6 100644
--- a/source/blender/editors/space_info/space_info.c
+++ b/source/blender/editors/space_info/space_info.c
@@ -255,11 +255,10 @@ static void info_header_region_message_subscribe(const wmRegionMessageSubscribeP
struct wmMsgBus *mbus = params->message_bus;
ARegion *region = params->region;
- wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
- .owner = region,
- .user_data = region,
- .notify = ED_region_do_msg_notify_tag_redraw,
- };
+ wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {NULL};
+ msg_sub_value_region_tag_redraw.owner = region;
+ msg_sub_value_region_tag_redraw.user_data = region;
+ msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw);
WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw);
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index eea81e425c2..7d4011e0812 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -266,7 +266,7 @@ static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float col
}
else if (strip->type == NLASTRIP_TYPE_META) {
/* Meta Clip */
- /* TODO: should temporary metas get different colors too? */
+ /* TODO: should temporary meta-strips get different colors too? */
if (strip->flag & NLASTRIP_FLAG_SELECT) {
/* selected - use a bold purple color */
UI_GetThemeColor3fv(TH_NLA_META_SEL, color);
@@ -408,8 +408,10 @@ static uint nla_draw_use_dashed_outlines(const float color[4], bool muted)
return shdr_pos;
}
-/** This check only accounts for the track's disabled flag and whether the strip is being tweaked.
- * It does not account for muting or soloing. */
+/**
+ * This check only accounts for the track's disabled flag and whether the strip is being tweaked.
+ * It does not account for muting or soloing.
+ */
static bool is_nlastrip_enabled(AnimData *adt, NlaTrack *nlt, NlaStrip *strip)
{
/** This shouldn't happen. If passed NULL, then just treat strip as enabled. */
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index bc043a4e665..6e234c5b2ce 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -37,20 +37,20 @@ set(INC
set(SRC
- drawnode.c
- node_add.c
+ drawnode.cc
+ node_add.cc
node_buttons.c
node_draw.cc
- node_edit.c
+ node_edit.cc
node_geometry_attribute_search.cc
node_gizmo.c
- node_group.c
+ node_group.cc
node_ops.c
- node_relationships.c
- node_select.c
- node_templates.c
- node_toolbar.c
- node_view.c
+ node_relationships.cc
+ node_select.cc
+ node_templates.cc
+ node_toolbar.cc
+ node_view.cc
space_node.c
node_intern.h
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.cc
index 6864d34885a..1d4c3b67387 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -95,9 +95,9 @@ static void node_socket_button_label(bContext *UNUSED(C),
static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
/* first output stores value */
- bNodeSocket *output = node->outputs.first;
+ bNodeSocket *output = (bNodeSocket *)node->outputs.first;
PointerRNA sockptr;
RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
@@ -106,15 +106,15 @@ static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *p
static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
/* first output stores value */
- bNodeSocket *output = node->outputs.first;
+ bNodeSocket *output = (bNodeSocket *)node->outputs.first;
PointerRNA sockptr;
uiLayout *col;
RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
col = uiLayoutColumn(layout, false);
- uiTemplateColorPicker(col, &sockptr, "default_value", 1, 0, 0, 0);
+ uiTemplateColorPicker(col, &sockptr, "default_value", true, false, false, false);
uiItemR(col, &sockptr, "default_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE);
}
@@ -129,14 +129,14 @@ static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA
uiItemR(row, ptr, "use_alpha", DEFAULT_FLAGS, "", ICON_IMAGE_RGB_ALPHA);
}
- uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
#if 0
/* XXX no context access here .. */
- bNode *node = ptr->data;
+ bNode *node = (bNode*)ptr->data;
CurveMapping *cumap = node->storage;
if (cumap) {
@@ -156,7 +156,7 @@ static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt
static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiTemplateColorRamp(layout, ptr, "color_ramp", 0);
+ uiTemplateColorRamp(layout, ptr, "color_ramp", false);
}
static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -179,8 +179,8 @@ void ED_node_sample_set(const float col[4])
static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
- CurveMapping *cumap = node->storage;
+ bNode *node = (bNode *)ptr->data;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
if (_sample_col[0] != SAMPLE_FLT_ISNONE) {
cumap->flag |= CUMA_DRAW_SAMPLE;
@@ -198,9 +198,9 @@ static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA
static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
/* first output stores normal */
- bNodeSocket *output = node->outputs.first;
+ bNodeSocket *output = (bNodeSocket *)node->outputs.first;
PointerRNA sockptr;
RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
@@ -209,7 +209,7 @@ static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *
static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
short multi = (node->id && ((Tex *)node->id)->use_nodes && (node->type != CMP_NODE_TEXTURE) &&
(node->type != TEX_NODE_TEXTURE));
@@ -233,14 +233,14 @@ static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), Po
if (!ELEM(RNA_enum_get(ptr, "interpolation_type"),
NODE_MAP_RANGE_SMOOTHSTEP,
NODE_MAP_RANGE_SMOOTHERSTEP)) {
- uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static int node_resize_area_default(bNode *node, int x, int y)
@@ -274,7 +274,7 @@ static int node_resize_area_default(bNode *node, int x, int y)
static void node_draw_buttons_group(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
uiTemplateIDBrowse(
- layout, C, ptr, "node_tree", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, NULL);
+ layout, C, ptr, "node_tree", nullptr, nullptr, nullptr, UI_TEMPLATE_ID_FILTER_ALL, nullptr);
}
/* XXX Does a bounding box update by iterating over all children.
@@ -311,7 +311,7 @@ static void node_draw_frame_prepare(const bContext *UNUSED(C), bNodeTree *ntree,
/* first child initializes frame */
if (bbinit) {
- bbinit = 0;
+ bbinit = false;
rect = noderect;
data->flag &= ~NODE_FRAME_RESIZEABLE;
}
@@ -418,9 +418,9 @@ static void node_draw_frame(const bContext *C,
{
/* skip if out of view */
- if (BLI_rctf_isect(&node->totr, &region->v2d.cur, NULL) == false) {
+ if (BLI_rctf_isect(&node->totr, &region->v2d.cur, nullptr) == false) {
UI_block_end(C, node->block);
- node->block = NULL;
+ node->block = nullptr;
return;
}
@@ -456,11 +456,13 @@ static void node_draw_frame(const bContext *C,
}
/* label */
- node_draw_frame_label(ntree, node, snode->runtime->aspect);
+ if (node->label[0] != '\0') {
+ node_draw_frame_label(ntree, node, snode->runtime->aspect);
+ }
UI_block_end(C, node->block);
UI_block_draw(C, node->block);
- node->block = NULL;
+ node->block = nullptr;
}
static int node_resize_area_frame(bNode *node, int x, int y)
@@ -495,7 +497,7 @@ static void node_buts_frame_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA
{
uiItemR(layout, ptr, "label_size", DEFAULT_FLAGS, IFACE_("Label Size"), ICON_NONE);
uiItemR(layout, ptr, "shrink", DEFAULT_FLAGS, IFACE_("Shrink"), ICON_NONE);
- uiItemR(layout, ptr, "text", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "text", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
#define NODE_REROUTE_SIZE 8.0f
@@ -509,11 +511,11 @@ static void node_draw_reroute_prepare(const bContext *UNUSED(C),
node_to_view(node, 0.0f, 0.0f, &locx, &locy);
/* reroute node has exactly one input and one output, both in the same place */
- bNodeSocket *nsock = node->outputs.first;
+ bNodeSocket *nsock = (bNodeSocket *)node->outputs.first;
nsock->locx = locx;
nsock->locy = locy;
- nsock = node->inputs.first;
+ nsock = (bNodeSocket *)node->inputs.first;
nsock->locx = locx;
nsock->locy = locy;
@@ -539,7 +541,7 @@ static void node_draw_reroute(const bContext *C,
if (node->totr.xmax < region->v2d.cur.xmin || node->totr.xmin > region->v2d.cur.xmax ||
node->totr.ymax < region->v2d.cur.ymin || node->totr.ymin > region->v2d.cur.ymax) {
UI_block_end(C, node->block);
- node->block = NULL;
+ node->block = nullptr;
return;
}
@@ -584,12 +586,12 @@ static void node_draw_reroute(const bContext *C,
(int)(rct->ymax),
(short)512,
(short)NODE_DY,
- NULL,
+ nullptr,
0,
0,
0,
0,
- NULL);
+ nullptr);
}
/* only draw input socket. as they all are placed on the same position.
@@ -599,7 +601,7 @@ static void node_draw_reroute(const bContext *C,
UI_block_end(C, node->block);
UI_block_draw(C, node->block);
- node->block = NULL;
+ node->block = nullptr;
}
/* Special tweak area for reroute node.
@@ -610,7 +612,7 @@ static int node_tweak_area_reroute(bNode *node, int x, int y)
/* square of tweak radius */
const float tweak_radius_sq = square_f(24.0f);
- bNodeSocket *sock = node->inputs.first;
+ bNodeSocket *sock = (bNodeSocket *)node->inputs.first;
float dx = sock->locx - x;
float dy = sock->locy - y;
return (dx * dx + dy * dy <= tweak_radius_sq);
@@ -660,28 +662,28 @@ static void node_buts_image_user(uiLayout *layout,
/* don't use iuser->framenr directly
* because it may not be updated if auto-refresh is off */
Scene *scene = CTX_data_scene(C);
- ImageUser *iuser = iuserptr->data;
+ ImageUser *iuser = (ImageUser *)iuserptr->data;
/* Image *ima = imaptr->data; */ /* UNUSED */
char numstr[32];
- const int framenr = BKE_image_user_frame_get(iuser, CFRA, NULL);
+ const int framenr = BKE_image_user_frame_get(iuser, CFRA, nullptr);
BLI_snprintf(numstr, sizeof(numstr), IFACE_("Frame: %d"), framenr);
uiItemL(layout, numstr, ICON_NONE);
}
if (ELEM(source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (show_layer_selection && RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER &&
RNA_boolean_get(ptr, "has_layers")) {
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "layer", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "layer", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (show_color_management) {
@@ -691,7 +693,7 @@ static void node_buts_image_user(uiLayout *layout,
uiItemR(split, &colorspace_settings_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE);
/* Avoid losing changes image is painted. */
- if (BKE_image_is_dirty(imaptr->data)) {
+ if (BKE_image_is_dirty((Image *)imaptr->data)) {
uiLayoutSetEnabled(split, false);
}
}
@@ -699,13 +701,13 @@ static void node_buts_image_user(uiLayout *layout,
static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_vector_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -715,7 +717,7 @@ static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), Po
static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(layout, ptr, "convert_from", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "convert_to", DEFAULT_FLAGS, "", ICON_NONE);
}
@@ -728,7 +730,7 @@ static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), Po
static void node_shader_buts_wireframe(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -743,10 +745,10 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA
"image",
"IMAGE_OT_new",
"IMAGE_OT_open",
- NULL,
+ nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
- NULL);
+ nullptr);
uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE);
@@ -765,7 +767,7 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA
static void node_shader_buts_tex_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user");
- uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0);
+ uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false);
}
static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -780,10 +782,10 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin
"image",
"IMAGE_OT_new",
"IMAGE_OT_open",
- NULL,
+ nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
- NULL);
+ nullptr);
uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE);
@@ -794,7 +796,7 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin
static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user");
- uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0);
+ uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false);
uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, IFACE_("Interpolation"), ICON_NONE);
uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, IFACE_("Projection"), ICON_NONE);
@@ -806,33 +808,33 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), Poin
if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_PREETHAM) {
uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_HOSEK) {
uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) {
- uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, nullptr, 0);
uiLayout *col;
if (RNA_boolean_get(ptr, "sun_disc")) {
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
@@ -843,7 +845,7 @@ static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C),
static void node_shader_buts_tex_magic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_tex_brick(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -899,32 +901,33 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- bNode *node = ptr->data;
- NodeShaderTexPointDensity *shader_point_density = node->storage;
+ bNode *node = (bNode *)ptr->data;
+ NodeShaderTexPointDensity *shader_point_density = (NodeShaderTexPointDensity *)node->storage;
Object *ob = (Object *)node->id;
PointerRNA ob_ptr, obdata_ptr;
RNA_id_pointer_create((ID *)ob, &ob_ptr);
- RNA_id_pointer_create(ob ? (ID *)ob->data : NULL, &obdata_ptr);
+ RNA_id_pointer_create(ob ? (ID *)ob->data : nullptr, &obdata_ptr);
- uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "object", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) {
PointerRNA dataptr;
RNA_id_pointer_create((ID *)node->id, &dataptr);
- uiItemPointerR(layout, ptr, "particle_system", &dataptr, "particle_systems", NULL, ICON_NONE);
+ uiItemPointerR(
+ layout, ptr, "particle_system", &dataptr, "particle_systems", nullptr, ICON_NONE);
}
- uiItemR(layout, ptr, "space", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "space", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) {
- uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
else {
- uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTWEIGHT) {
if (ob_ptr.data) {
uiItemPointerR(
@@ -942,18 +945,18 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout,
static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, 0);
- uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "object", DEFAULT_FLAGS, nullptr, 0);
+ uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_bump(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, nullptr, 0);
if (!RNA_boolean_get(ptr, "from_instancer")) {
PointerRNA obptr = CTX_data_pointer_get(C, "active_object");
@@ -987,7 +990,7 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer
static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -1034,7 +1037,7 @@ static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA *
}
}
else {
- uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, 0);
+ uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, 0);
}
}
@@ -1081,7 +1084,7 @@ static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerR
uiLayout *row;
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
@@ -1098,7 +1101,7 @@ static void node_shader_buts_script(uiLayout *layout, bContext *UNUSED(C), Point
uiLayout *row;
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
@@ -1120,7 +1123,7 @@ static void node_shader_buts_script_ex(uiLayout *layout, bContext *C, PointerRNA
#if 0 /* not implemented yet */
if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_EXTERNAL) {
- uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
#endif
}
@@ -1137,21 +1140,21 @@ static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), Po
col = uiLayoutColumn(layout, false);
row = uiLayoutRow(col, true);
uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_bevel(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_ambient_occlusion(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1161,7 +1164,7 @@ static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C),
static void node_shader_buts_output_aov(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "name", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "name", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
/* only once called */
@@ -1346,17 +1349,17 @@ static void node_buts_image_views(uiLayout *layout,
if (RNA_boolean_get(ptr, "has_views")) {
if (RNA_enum_get(ptr, "view") == 0) {
- uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_CAMERA_STEREO);
+ uiItemR(col, ptr, "view", DEFAULT_FLAGS, nullptr, ICON_CAMERA_STEREO);
}
else {
- uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_SCENE);
+ uiItemR(col, ptr, "view", DEFAULT_FLAGS, nullptr, ICON_SCENE);
}
}
}
static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
PointerRNA iuserptr;
RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
@@ -1367,10 +1370,10 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *
"image",
"IMAGE_OT_new",
"IMAGE_OT_open",
- NULL,
+ nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
- NULL);
+ nullptr);
if (!node->id) {
return;
}
@@ -1384,20 +1387,29 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *
static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
PointerRNA iuserptr;
RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
uiLayoutSetContextPointer(layout, "image_user", &iuserptr);
- uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 1);
+ uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, true);
}
static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
uiLayout *col, *row;
- uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "scene",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (!node->id) {
return;
@@ -1421,7 +1433,7 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer
PointerRNA op_ptr;
uiItemFullO(
- row, "RENDER_OT_render", "", ICON_RENDER_STILL, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
+ row, "RENDER_OT_render", "", ICON_RENDER_STILL, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_string_set(&op_ptr, "layer", layer_name);
RNA_string_set(&op_ptr, "scene", scene_name);
}
@@ -1436,19 +1448,19 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(col, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE);
if (filter != R_FILTER_FAST_GAUSS) {
- uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (!reference) {
- uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
- uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
- uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_boolean_get(ptr, "use_relative")) {
uiItemL(col, IFACE_("Aspect Correction"), ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "factor_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE);
@@ -1459,15 +1471,15 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(col, ptr, "size_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE);
uiItemR(col, ptr, "size_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE);
}
- uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *col;
- uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
uiItemL(col, IFACE_("Center:"), ICON_NONE);
@@ -1477,13 +1489,13 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemS(layout);
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_bilateralblur(uiLayout *layout,
@@ -1493,9 +1505,9 @@ static void node_composit_buts_bilateralblur(uiLayout *layout,
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -1505,27 +1517,47 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA
col = uiLayoutColumn(layout, false);
uiItemL(col, IFACE_("Bokeh Type:"), ICON_NONE);
uiItemR(col, ptr, "bokeh", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_zbuffer") == true);
- uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "scene",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, nullptr, ICON_NONE);
sub = uiLayoutColumn(col, false);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_zbuffer") == false);
- uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, nullptr, ICON_NONE);
+}
+
+static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayout *col;
+
+ col = uiLayoutColumn(layout, false);
+
+ uiItemR(col, ptr, "threshold", 0, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "contrast_limit", 0, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE);
}
/* qdn: glare node */
@@ -1535,29 +1567,30 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemR(layout, ptr, "quality", DEFAULT_FLAGS, "", ICON_NONE);
if (RNA_enum_get(ptr, "glare_type") != 1) {
- uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "glare_type") != 0) {
- uiItemR(layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(
+ layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
}
- uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "glare_type") == 2) {
- uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (RNA_enum_get(ptr, "glare_type") == 0 || RNA_enum_get(ptr, "glare_type") == 2) {
- uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "glare_type") == 0) {
- uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
if (RNA_enum_get(ptr, "glare_type") == 1) {
- uiItemR(layout, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "size", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
@@ -1568,15 +1601,15 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po
col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "tonemap_type", DEFAULT_FLAGS, "", ICON_NONE);
if (RNA_enum_get(ptr, "tonemap_type") == 0) {
- uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
else {
- uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
}
@@ -1585,12 +1618,12 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(col, false);
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_projector") == false);
- uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1598,7 +1631,7 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
uiItemR(col, ptr, "factor", DEFAULT_FLAGS, IFACE_("Blur"), ICON_NONE);
col = uiLayoutColumn(layout, true);
@@ -1606,7 +1639,7 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(col, ptr, "speed_min", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
uiItemR(col, ptr, "speed_max", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
- uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1623,8 +1656,8 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point
{
uiLayout *col;
- uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
if (RNA_boolean_get(ptr, "relative")) {
@@ -1647,8 +1680,8 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C)
col = uiLayoutColumn(layout, false);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(col, ptr, "factor", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "factor", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_double_edge_mask(uiLayout *layout,
@@ -1670,7 +1703,7 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1678,17 +1711,17 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C),
uiLayout *sub, *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "size", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, nullptr, ICON_NONE);
sub = uiLayoutColumn(col, false);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min"));
uiItemR(sub, ptr, "min", DEFAULT_FLAGS, "", ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, nullptr, ICON_NONE);
sub = uiLayoutColumn(col, false);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max"));
uiItemR(sub, ptr, "max", DEFAULT_FLAGS, "", ICON_NONE);
@@ -1699,8 +1732,8 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "premul", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "premul", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1708,27 +1741,27 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
switch (RNA_enum_get(ptr, "mode")) {
case CMP_NODE_DILATEERODE_DISTANCE_THRESH:
- uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, nullptr, ICON_NONE);
break;
case CMP_NODE_DILATEERODE_DISTANCE_FEATHER:
- uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, nullptr, ICON_NONE);
break;
}
}
static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1736,8 +1769,8 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1745,8 +1778,8 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_distance_matte(uiLayout *layout,
@@ -1759,10 +1792,10 @@ static void node_composit_buts_distance_matte(uiLayout *layout,
uiItemL(layout, IFACE_("Color Space:"), ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
- uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1771,23 +1804,23 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C)
uiItemL(layout, IFACE_("Despill Channel:"), ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "limit_method") == 0) {
uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
- uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_boolean_get(ptr, "use_unspill") == true) {
- uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
}
@@ -1796,13 +1829,13 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
- /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now */
- uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now*/
+ /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); Removed for now */
+ uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); Removed for now*/
}
static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1810,9 +1843,9 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C)
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_channel_matte(uiLayout *layout,
@@ -1823,24 +1856,24 @@ static void node_composit_buts_channel_matte(uiLayout *layout,
uiItemL(layout, IFACE_("Color Space:"), ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
uiItemL(col, IFACE_("Key Channel:"), ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "limit_method") == 0) {
uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
- uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1848,19 +1881,19 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "index", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "index", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1892,7 +1925,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
/* disable stereo output for multilayer, too much work for something that no one will use */
/* if someone asks for that we can implement it */
if (is_multiview) {
- uiTemplateImageFormatViews(layout, &imfptr, NULL);
+ uiTemplateImageFormatViews(layout, &imfptr, nullptr);
}
uiItemS(layout);
@@ -1913,7 +1946,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
"layer_slots",
ptr,
"active_input_index",
- NULL,
+ nullptr,
0,
0,
0,
@@ -1932,7 +1965,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
"file_slots",
ptr,
"active_input_index",
- NULL,
+ nullptr,
0,
0,
0,
@@ -1948,9 +1981,9 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
col = uiLayoutColumn(row, true);
wmOperatorType *ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false);
- uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
+ uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_enum_set(&op_ptr, "direction", 1);
- uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
+ uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_enum_set(&op_ptr, "direction", 2);
if (active_input_ptr.data) {
@@ -1964,10 +1997,10 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
"NODE_OT_output_file_remove_active_socket",
"",
ICON_X,
- NULL,
+ nullptr,
WM_OP_EXEC_DEFAULT,
UI_ITEM_R_ICON_ONLY,
- NULL);
+ nullptr);
}
else {
col = uiLayoutColumn(layout, true);
@@ -1979,23 +2012,23 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
"NODE_OT_output_file_remove_active_socket",
"",
ICON_X,
- NULL,
+ nullptr,
WM_OP_EXEC_DEFAULT,
UI_ITEM_R_ICON_ONLY,
- NULL);
+ nullptr);
/* format details for individual files */
imfptr = RNA_pointer_get(&active_input_ptr, "format");
col = uiLayoutColumn(layout, true);
uiItemL(col, IFACE_("Format:"), ICON_NONE);
- uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, nullptr, ICON_NONE);
const bool is_socket_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR;
const bool use_node_format = RNA_boolean_get(&active_input_ptr, "use_node_format");
if ((!is_exr && use_node_format) || (!is_socket_exr && !use_node_format)) {
- uiItemR(col, &active_input_ptr, "save_as_render", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, &active_input_ptr, "save_as_render", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
col = uiLayoutColumn(layout, false);
@@ -2003,7 +2036,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
uiTemplateImageSettings(col, &imfptr, false);
if (is_multiview) {
- uiTemplateImageFormatViews(layout, &imfptr, NULL);
+ uiTemplateImageFormatViews(layout, &imfptr, nullptr);
}
}
}
@@ -2015,7 +2048,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) {
uiLayout *row;
- uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
uiItemR(row, ptr, "offset_x", DEFAULT_FLAGS, "X", ICON_NONE);
uiItemR(row, ptr, "offset_y", DEFAULT_FLAGS, "Y", ICON_NONE);
@@ -2032,8 +2065,8 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -2043,86 +2076,86 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C),
static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *split, *col, *row;
- uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "correction_method") == 0) {
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "lift", 1, 1, 0, 1);
+ uiTemplateColorPicker(col, ptr, "lift", true, true, false, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "lift", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "gamma", 1, 1, 1, 1);
+ uiTemplateColorPicker(col, ptr, "gamma", true, true, true, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "gain", 1, 1, 1, 1);
+ uiTemplateColorPicker(col, ptr, "gain", true, true, true, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "gain", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
else {
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "offset", 1, 1, 0, 1);
+ uiTemplateColorPicker(col, ptr, "offset", true, true, false, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "power", 1, 1, 0, 1);
+ uiTemplateColorPicker(col, ptr, "power", true, true, false, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "power", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "slope", 1, 1, 0, 1);
+ uiTemplateColorPicker(col, ptr, "slope", true, true, false, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "slope", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_composit_buts_colorbalance_ex(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "correction_method") == 0) {
- uiTemplateColorPicker(layout, ptr, "lift", 1, 1, 0, 1);
- uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true);
+ uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateColorPicker(layout, ptr, "gamma", 1, 1, 1, 1);
- uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "gamma", true, true, true, true);
+ uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateColorPicker(layout, ptr, "gain", 1, 1, 1, 1);
- uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "gain", true, true, true, true);
+ uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
else {
- uiTemplateColorPicker(layout, ptr, "offset", 1, 1, 0, 1);
- uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "offset", true, true, false, true);
+ uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateColorPicker(layout, ptr, "power", 1, 1, 0, 1);
- uiItemR(layout, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "power", true, true, false, true);
+ uiItemR(layout, ptr, "power", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateColorPicker(layout, ptr, "slope", 1, 1, 0, 1);
- uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "slope", true, true, false, true);
+ uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
- CurveMapping *cumap = node->storage;
+ bNode *node = (bNode *)ptr->data;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
if (_sample_col[0] != SAMPLE_FLT_ISNONE) {
cumap->flag |= CUMA_DRAW_SAMPLE;
@@ -2142,17 +2175,33 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe
static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
}
static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
PointerRNA clipptr;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (!node->id) {
return;
@@ -2165,23 +2214,31 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point
static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (!node->id) {
return;
}
uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -2191,10 +2248,18 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C),
static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (!node->id) {
return;
@@ -2210,9 +2275,9 @@ static void node_composit_buts_colorcorrection(uiLayout *layout,
uiLayout *row;
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "red", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "green", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "blue", DEFAULT_FLAGS, nullptr, ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemL(row, "", ICON_NONE);
@@ -2255,8 +2320,8 @@ static void node_composit_buts_colorcorrection(uiLayout *layout,
uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_colorcorrection_ex(uiLayout *layout,
@@ -2266,53 +2331,53 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout,
uiLayout *row;
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "red", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "green", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "blue", DEFAULT_FLAGS, nullptr, ICON_NONE);
row = layout;
uiItemL(row, IFACE_("Saturation"), ICON_NONE);
- uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
uiItemL(row, IFACE_("Contrast"), ICON_NONE);
- uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
uiItemL(row, IFACE_("Gamma"), ICON_NONE);
- uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
uiItemL(row, IFACE_("Gain"), ICON_NONE);
- uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
uiItemL(row, IFACE_("Lift"), ICON_NONE);
- uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "check", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "check", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_switch_view_ex(uiLayout *layout,
@@ -2323,10 +2388,10 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout,
"NODE_OT_switch_view_update",
"Update Views",
ICON_FILE_REFRESH,
- NULL,
+ nullptr,
WM_OP_INVOKE_DEFAULT,
0,
- NULL);
+ nullptr);
}
static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -2334,32 +2399,32 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po
uiLayout *row;
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "x", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "y", DEFAULT_FLAGS, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE);
- // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE); /* UNUSED */
- uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, nullptr, ICON_NONE); /* UNUSED */
+ uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_backdrop_viewer(
@@ -2394,7 +2459,7 @@ static void node_composit_backdrop_viewer(
static void node_composit_backdrop_boxmask(
SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y)
{
- NodeBoxMask *boxmask = node->storage;
+ NodeBoxMask *boxmask = (NodeBoxMask *)node->storage;
const float backdropWidth = backdrop->x;
const float backdropHeight = backdrop->y;
const float aspect = backdropWidth / backdropHeight;
@@ -2439,7 +2504,7 @@ static void node_composit_backdrop_boxmask(
static void node_composit_backdrop_ellipsemask(
SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y)
{
- NodeEllipseMask *ellipsemask = node->storage;
+ NodeEllipseMask *ellipsemask = (NodeEllipseMask *)node->storage;
const float backdropWidth = backdrop->x;
const float backdropHeight = backdrop->y;
const float aspect = backdropWidth / backdropHeight;
@@ -2485,65 +2550,83 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C)
{
uiLayout *row;
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "x", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "y", DEFAULT_FLAGS, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_viewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *col;
- uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "tile_order") == 0) {
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
- uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "mask",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
+ uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, nullptr, ICON_NONE);
uiItemR(layout, ptr, "size_source", DEFAULT_FLAGS, "", ICON_NONE);
if (node->custom1 & (CMP_NODEFLAG_MASK_FIXED | CMP_NODEFLAG_MASK_FIXED_SCENE)) {
- uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
- uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) {
- uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (node->id) {
MovieClip *clip = (MovieClip *)node->id;
@@ -2559,28 +2642,36 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point
static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- /* bNode *node = ptr->data; */ /* UNUSED */
+ /* bNode *node = (bNode*)ptr->data; */ /* UNUSED */
- uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (node->id) {
MovieClip *clip = (MovieClip *)node->id;
@@ -2588,7 +2679,7 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
MovieTrackingObject *object;
uiLayout *col;
PointerRNA tracking_ptr;
- NodeTrackPosData *data = node->storage;
+ NodeTrackPosData *data = (NodeTrackPosData *)node->storage;
RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr);
@@ -2607,21 +2698,29 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
uiItemR(layout, ptr, "track_name", DEFAULT_FLAGS, "", ICON_ANIM_DATA);
}
- uiItemR(layout, ptr, "position", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "position", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (ELEM(node->custom1, CMP_TRACKPOS_RELATIVE_FRAME, CMP_TRACKPOS_ABSOLUTE_FRAME)) {
- uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
}
static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
- NodePlaneTrackDeformData *data = node->storage;
+ bNode *node = (bNode *)ptr->data;
+ NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)node->storage;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (node->id) {
MovieClip *clip = (MovieClip *)node->id;
@@ -2649,10 +2748,10 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P
}
}
- uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) {
- uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
@@ -2665,7 +2764,7 @@ static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout),
static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, "", ICON_NONE);
- uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_cryptomatte_legacy(uiLayout *layout,
@@ -2693,18 +2792,35 @@ static void node_composit_buts_cryptomatte_legacy_ex(uiLayout *layout,
static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
uiLayout *row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiLayout *col = uiLayoutColumn(layout, false);
if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) {
- uiTemplateID(col, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(col,
+ C,
+ ptr,
+ "scene",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
}
else {
- uiTemplateID(
- col, C, ptr, "image", NULL, "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(col,
+ C,
+ ptr,
+ "image",
+ nullptr,
+ "IMAGE_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
NodeCryptomatte *crypto = (NodeCryptomatte *)node->storage;
PointerRNA imaptr = RNA_pointer_get(ptr, "image");
@@ -2730,7 +2846,7 @@ static void node_composit_buts_brightcontrast(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -2738,12 +2854,15 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po
#ifndef WITH_OPENIMAGEDENOISE
uiItemL(layout, IFACE_("Disabled, built without OpenImageDenoise"), ICON_ERROR);
#else
+ /* Always supported through Accelerate framework BNNS on macOS. */
+# ifndef __APPLE__
if (!BLI_cpu_support_sse41()) {
uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR);
}
+# endif
#endif
- uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
/* only once called */
@@ -2799,6 +2918,9 @@ static void node_composit_set_butfunc(bNodeType *ntype)
case CMP_NODE_DEFOCUS:
ntype->draw_buttons = node_composit_buts_defocus;
break;
+ case CMP_NODE_ANTIALIASING:
+ ntype->draw_buttons = node_composit_buts_antialiasing;
+ break;
case CMP_NODE_GLARE:
ntype->draw_buttons = node_composit_buts_glare;
break;
@@ -3010,7 +3132,7 @@ static void node_texture_buts_bricks(uiLayout *layout, bContext *UNUSED(C), Poin
static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
PointerRNA tex_ptr;
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
ID *id = ptr->owner_id;
Tex *tex = (Tex *)node->storage;
uiLayout *col, *row;
@@ -3023,29 +3145,31 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe
case TEX_BLEND:
uiItemR(col, &tex_ptr, "progression", DEFAULT_FLAGS, "", ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(
+ row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
break;
case TEX_MARBLE:
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
uiItemR(row, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(
+ row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
break;
case TEX_MAGIC:
- uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, nullptr, ICON_NONE);
break;
case TEX_STUCCI:
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE);
break;
@@ -3053,18 +3177,19 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe
uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(col, &tex_ptr, "wood_type", DEFAULT_FLAGS, "", ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(
+ row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
uiLayoutSetActive(row, !(ELEM(tex->stype, TEX_BAND, TEX_RING)));
- uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
break;
case TEX_CLOUDS:
uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(col,
&tex_ptr,
"noise_depth",
@@ -3085,7 +3210,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe
case TEX_VORONOI:
uiItemR(col, &tex_ptr, "distance_metric", DEFAULT_FLAGS, "", ICON_NONE);
if (tex->vn_distm == TEX_MINKOVSKY) {
- uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
uiItemR(col, &tex_ptr, "color_mode", DEFAULT_FLAGS, "", ICON_NONE);
break;
@@ -3100,19 +3225,19 @@ static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *p
"image",
"IMAGE_OT_new",
"IMAGE_OT_open",
- NULL,
+ nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
- NULL);
+ nullptr);
}
static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
PointerRNA iuserptr;
RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
- uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0);
+ uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false);
}
static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -3169,12 +3294,16 @@ static void node_texture_set_butfunc(bNodeType *ntype)
}
}
-/* ****** init draw callbacks for all tree types, only called in usiblender.c, once ************ */
+/* -------------------------------------------------------------------- */
+/** \name Init Draw Callbacks For All Tree Types
+ *
+ * Only called on node initialization, once.
+ * \{ */
static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
ED_node_tag_update_nodetree(bmain, ntree, node);
}
@@ -3184,7 +3313,7 @@ static void node_socket_template_properties_update(bNodeType *ntype, bNodeSocket
PropertyRNA *prop = RNA_struct_type_find_property(srna, stemp->identifier);
if (prop) {
- RNA_def_property_update_runtime(prop, node_property_update_default);
+ RNA_def_property_update_runtime(prop, (const void *)node_property_update_default);
}
}
@@ -3241,6 +3370,8 @@ static void node_socket_undefined_interface_draw_color(bContext *UNUSED(C),
r_color[3] = 1.0f;
}
+/** \} */
+
void ED_node_init_butfuncs(void)
{
/* Fallback types for undefined tree, nodes, sockets
@@ -3252,8 +3383,8 @@ void ED_node_init_butfuncs(void)
NodeTypeUndefined.draw_nodetype_prepare = node_update_default;
NodeTypeUndefined.select_area_func = node_select_area_default;
NodeTypeUndefined.tweak_area_func = node_tweak_area_default;
- NodeTypeUndefined.draw_buttons = NULL;
- NodeTypeUndefined.draw_buttons_ex = NULL;
+ NodeTypeUndefined.draw_buttons = nullptr;
+ NodeTypeUndefined.draw_buttons_ex = nullptr;
NodeTypeUndefined.resize_area_func = node_resize_area_default;
NodeSocketTypeUndefined.draw = node_socket_undefined_draw;
@@ -3314,12 +3445,14 @@ static const float std_node_socket_colors[][4] = {
{0.39, 0.78, 0.39, 1.0}, /* SOCK_SHADER */
{0.80, 0.65, 0.84, 1.0}, /* SOCK_BOOLEAN */
{0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */
- {0.25, 0.75, 0.26, 1.0}, /* SOCK_INT */
+ {0.35, 0.55, 0.36, 1.0}, /* SOCK_INT */
{0.44, 0.70, 1.00, 1.0}, /* SOCK_STRING */
{0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */
- {0.89, 0.76, 0.43, 1.0}, /* SOCK_IMAGE */
+ {0.39, 0.22, 0.39, 1.0}, /* SOCK_IMAGE */
{0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */
{0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */
+ {0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */
+ {0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */
};
/* common color callbacks for standard types */
@@ -3328,7 +3461,7 @@ static void std_node_socket_draw_color(bContext *UNUSED(C),
PointerRNA *UNUSED(node_ptr),
float *r_color)
{
- bNodeSocket *sock = ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
copy_v4_v4(r_color, std_node_socket_colors[type]);
}
@@ -3336,7 +3469,7 @@ static void std_node_socket_interface_draw_color(bContext *UNUSED(C),
PointerRNA *ptr,
float *r_color)
{
- bNodeSocket *sock = ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
copy_v4_v4(r_color, std_node_socket_colors[type]);
}
@@ -3349,7 +3482,7 @@ static void node_file_output_socket_draw(bContext *C,
PointerRNA *node_ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
- bNodeSocket *sock = ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
uiLayout *row;
PointerRNA inputptr;
@@ -3359,13 +3492,13 @@ static void node_file_output_socket_draw(bContext *C,
int imtype = RNA_enum_get(&imfptr, "file_format");
if (imtype == R_IMF_IMTYPE_MULTILAYER) {
- NodeImageMultiFileSocket *input = sock->storage;
+ NodeImageMultiFileSocket *input = (NodeImageMultiFileSocket *)sock->storage;
RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr);
uiItemL(row, input->layer, ICON_NONE);
}
else {
- NodeImageMultiFileSocket *input = sock->storage;
+ NodeImageMultiFileSocket *input = (NodeImageMultiFileSocket *)sock->storage;
uiBlock *block;
RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotFile, input, &inputptr);
@@ -3392,8 +3525,8 @@ static void node_file_output_socket_draw(bContext *C,
static void std_node_socket_draw(
bContext *C, uiLayout *layout, PointerRNA *ptr, PointerRNA *node_ptr, const char *text)
{
- bNode *node = node_ptr->data;
- bNodeSocket *sock = ptr->data;
+ bNode *node = (bNode *)node_ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
/*int subtype = sock->typeinfo->subtype;*/
@@ -3408,6 +3541,8 @@ static void std_node_socket_draw(
return;
}
+ text = (sock->flag & SOCK_HIDE_LABEL) ? "" : text;
+
switch (type) {
case SOCK_FLOAT:
case SOCK_INT:
@@ -3440,7 +3575,7 @@ static void std_node_socket_draw(
const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id;
if (node_tree->type == NTREE_GEOMETRY) {
- node_geometry_add_attribute_search_button(node_tree, node, ptr, row);
+ node_geometry_add_attribute_search_button(C, node_tree, node, ptr, row);
}
else {
uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0);
@@ -3460,6 +3595,15 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
+ case SOCK_TEXTURE: {
+ uiTemplateID(
+ layout, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
+ break;
+ }
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
@@ -3468,7 +3612,7 @@ static void std_node_socket_draw(
static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout, PointerRNA *ptr)
{
- bNodeSocket *sock = ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
uiLayout *col = uiLayoutColumn(layout, false);
@@ -3503,7 +3647,7 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout
}
}
- uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, nullptr, 0);
}
void ED_init_standard_node_socket_type(bNodeSocketType *stype)
@@ -3570,7 +3714,7 @@ void draw_nodespace_back_pix(const bContext *C,
void *lock;
Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (ibuf) {
/* somehow the offset has to be calculated inverse */
wmOrtho2_region_pixelspace(region);
@@ -3579,7 +3723,7 @@ void draw_nodespace_back_pix(const bContext *C,
/** \note draw selected info on backdrop */
if (snode->edittree) {
- bNode *node = snode->edittree->nodes.first;
+ bNode *node = (bNode *)snode->edittree->nodes.first;
rctf *viewer_border = &snode->nodetree->viewer_border;
while (node) {
if (node->flag & NODE_SELECT) {
@@ -3616,7 +3760,7 @@ void draw_nodespace_back_pix(const bContext *C,
GPU_matrix_pop();
}
-/* return quadratic beziers points for a given nodelink and clip if v2d is not NULL. */
+/* return quadratic beziers points for a given nodelink and clip if v2d is not nullptr. */
bool node_link_bezier_handles(const View2D *v2d,
const SpaceNode *snode,
const bNodeLink *link,
@@ -3646,7 +3790,7 @@ bool node_link_bezier_handles(const View2D *v2d,
fromreroute = (link->fromnode && link->fromnode->type == NODE_REROUTE);
}
else {
- if (snode == NULL) {
+ if (snode == nullptr) {
return false;
}
copy_v2_v2(vec[0], cursor);
@@ -3665,7 +3809,7 @@ bool node_link_bezier_handles(const View2D *v2d,
toreroute = (link->tonode && link->tonode->type == NODE_REROUTE);
}
else {
- if (snode == NULL) {
+ if (snode == nullptr) {
return false;
}
copy_v2_v2(vec[3], cursor);
@@ -3725,7 +3869,7 @@ bool node_link_bezier_handles(const View2D *v2d,
return true;
}
-/* if v2d not NULL, it clips and returns 0 if not visible */
+/* if v2d not nullptr, it clips and returns 0 if not visible */
bool node_link_bezier_points(const View2D *v2d,
const SpaceNode *snode,
const bNodeLink *link,
@@ -3758,6 +3902,7 @@ static float arrow_expand_axis[3][2] = {{0.7071f, 0.7071f}, {M_SQRT2, 0.0f}, {0.
static float mute_verts[3][2] = {{0.7071f, 1.0f}, {0.7071f, 0.0f}, {0.7071f, -1.0f}};
static float mute_expand_axis[3][2] = {{1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, -0.0f}};
+/* Is zero initialized because it is static data. */
static struct {
GPUBatch *batch; /* for batching line together */
GPUBatch *batch_single; /* for single line */
@@ -3768,9 +3913,9 @@ static struct {
GPUVertBufRaw colid_step, muted_step;
uint count;
bool enabled;
-} g_batch_link = {0};
+} g_batch_link;
-static void nodelink_batch_reset(void)
+static void nodelink_batch_reset()
{
GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p0_id, &g_batch_link.p0_step);
GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p1_id, &g_batch_link.p1_step);
@@ -3797,7 +3942,7 @@ static void set_nodelink_vertex(GPUVertBuf *vbo,
GPU_vertbuf_attr_set(vbo, exp_id, v, exp);
}
-static void nodelink_batch_init(void)
+static void nodelink_batch_init()
{
GPUVertFormat format = {0};
uint uv_id = GPU_vertformat_attr_add(&format, "uv", GPU_COMP_U8, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
@@ -3876,10 +4021,11 @@ static void nodelink_batch_init(void)
}
}
- g_batch_link.batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
+ g_batch_link.batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_OWNS_VBO);
gpu_batch_presets_register(g_batch_link.batch);
- g_batch_link.batch_single = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, 0);
+ g_batch_link.batch_single = GPU_batch_create_ex(
+ GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_INVALID);
gpu_batch_presets_register(g_batch_link.batch_single);
/* Instances data */
@@ -3979,16 +4125,16 @@ static void nodelink_batch_add_link(const SpaceNode *snode,
BLI_assert(ELEM(th_col3, TH_WIRE, TH_REDALERT, -1));
g_batch_link.count++;
- copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0);
- copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1);
- copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2);
- copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3);
- char *colid = GPU_vertbuf_raw_step(&g_batch_link.colid_step);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3);
+ char *colid = (char *)GPU_vertbuf_raw_step(&g_batch_link.colid_step);
colid[0] = nodelink_get_color_id(th_col1);
colid[1] = nodelink_get_color_id(th_col2);
colid[2] = nodelink_get_color_id(th_col3);
colid[3] = drawarrow;
- char *muted = GPU_vertbuf_raw_step(&g_batch_link.muted_step);
+ char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step);
muted[0] = drawmuted;
if (g_batch_link.count == NODELINK_GROUP_SIZE) {
@@ -4010,7 +4156,7 @@ void node_draw_link_bezier(const View2D *v2d,
int drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) &&
(link->fromnode && (link->fromnode->type == NODE_REROUTE)));
int drawmuted = (link->flag & NODE_LINK_MUTED);
- if (g_batch_link.batch == NULL) {
+ if (g_batch_link.batch == nullptr) {
nodelink_batch_init();
}
@@ -4052,7 +4198,7 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
{
int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE;
- if (link->fromsock == NULL && link->tosock == NULL) {
+ if (link->fromsock == nullptr && link->tosock == nullptr) {
return;
}
diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.cc
index c4fe9e9e531..6143af8ed70 100644
--- a/source/blender/editors/space_node/node_add.c
+++ b/source/blender/editors/space_node/node_add.cc
@@ -41,6 +41,8 @@
#include "BKE_scene.h"
#include "BKE_texture.h"
+#include "DEG_depsgraph_build.h"
+
#include "ED_node.h" /* own include */
#include "ED_render.h"
#include "ED_screen.h"
@@ -63,13 +65,13 @@
/**
* XXX Does some additional initialization on top of #nodeAddNode
* Can be used with both custom and static nodes,
- * if `idname == NULL` the static int type will be used instead.
+ * if `idname == nullptr` the static int type will be used instead.
*/
bNode *node_add_node(const bContext *C, const char *idname, int type, float locx, float locy)
{
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main(C);
- bNode *node = NULL;
+ bNode *node = nullptr;
node_deselect_all(snode);
@@ -88,7 +90,7 @@ bNode *node_add_node(const bContext *C, const char *idname, int type, float locx
nodeSetSelected(node, true);
ntreeUpdateTree(bmain, snode->edittree);
- ED_node_set_active(bmain, snode->edittree, node, NULL);
+ ED_node_set_active(bmain, snode->edittree, node, nullptr);
snode_update(snode, node);
@@ -112,7 +114,7 @@ static bool add_reroute_intersect_check(bNodeLink *link,
{
float coord_array[NODE_LINK_RESOL + 1][2];
- if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
+ if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) {
for (int i = 0; i < tot - 1; i++) {
for (int b = 0; b < NODE_LINK_RESOL; b++) {
if (isect_seg_seg_v2_point(
@@ -125,13 +127,13 @@ static bool add_reroute_intersect_check(bNodeLink *link,
return false;
}
-typedef struct bNodeSocketLink {
+struct bNodeSocketLink {
struct bNodeSocketLink *next, *prev;
struct bNodeSocket *sock;
struct bNodeLink *link;
float point[2];
-} bNodeSocketLink;
+};
static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb,
bNodeSocket *sock,
@@ -140,12 +142,12 @@ static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb,
{
bNodeSocketLink *socklink, *prev;
- socklink = MEM_callocN(sizeof(bNodeSocketLink), "socket link");
+ socklink = (bNodeSocketLink *)MEM_callocN(sizeof(bNodeSocketLink), "socket link");
socklink->sock = sock;
socklink->link = link;
copy_v2_v2(socklink->point, point);
- for (prev = lb->last; prev; prev = prev->prev) {
+ for (prev = (bNodeSocketLink *)lb->last; prev; prev = prev->prev) {
if (prev->sock == sock) {
break;
}
@@ -160,7 +162,7 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C,
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNode *reroute_node = NULL;
+ bNode *reroute_node = nullptr;
bNodeSocket *cursock = socklink->sock;
float insert_point[2];
int num_links;
@@ -182,12 +184,12 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C,
socklink->link->fromnode,
socklink->link->fromsock,
reroute_node,
- reroute_node->inputs.first);
+ (bNodeSocket *)reroute_node->inputs.first);
}
else {
nodeAddLink(ntree,
reroute_node,
- reroute_node->outputs.first,
+ (bNodeSocket *)reroute_node->outputs.first,
socklink->link->tonode,
socklink->link->tosock);
}
@@ -196,11 +198,11 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C,
/* insert the reroute node into the link */
if (in_out == SOCK_OUT) {
socklink->link->fromnode = reroute_node;
- socklink->link->fromsock = reroute_node->outputs.first;
+ socklink->link->fromsock = (bNodeSocket *)reroute_node->outputs.first;
}
else {
socklink->link->tonode = reroute_node;
- socklink->link->tosock = reroute_node->inputs.first;
+ socklink->link->tosock = (bNodeSocket *)reroute_node->inputs.first;
}
add_v2_v2(insert_point, socklink->point);
@@ -257,7 +259,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
BLI_listbase_clear(&output_links);
BLI_listbase_clear(&input_links);
- for (link = ntree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)ntree->links.first; link; link = link->next) {
if (nodeLinkIsHidden(link)) {
continue;
}
@@ -273,11 +275,11 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
/* Create reroute nodes for intersected links.
* Only one reroute if links share the same input/output socket.
*/
- socklink = output_links.first;
+ socklink = (bNodeSocketLink *)output_links.first;
while (socklink) {
socklink = add_reroute_do_socket_section(C, socklink, SOCK_OUT);
}
- socklink = input_links.first;
+ socklink = (bNodeSocketLink *)input_links.first;
while (socklink) {
socklink = add_reroute_do_socket_section(C, socklink, SOCK_IN);
}
@@ -315,7 +317,7 @@ void NODE_OT_add_reroute(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
/* internal */
RNA_def_int(ot->srna, "cursor", WM_CURSOR_CROSS, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
}
@@ -335,10 +337,28 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain,
bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name);
if (!node_group) {
- return NULL;
- }
- if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group)) {
- return NULL;
+ return nullptr;
+ }
+
+ const char *disabled_hint = nullptr;
+ if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) {
+ if (disabled_hint) {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Can not add node group '%s' to '%s':\n %s",
+ node_group->id.name + 2,
+ ntree->id.name + 2,
+ disabled_hint);
+ }
+ else {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Can not add node group '%s' to '%s'",
+ node_group->id.name + 2,
+ ntree->id.name + 2);
+ }
+
+ return nullptr;
}
return node_group;
@@ -430,7 +450,7 @@ static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOper
Object *object = (Object *)BKE_libblock_find_name(bmain, ID_OB, name);
if (!object) {
- return NULL;
+ return nullptr;
}
return object;
@@ -450,7 +470,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
bNode *object_node = node_add_node(
- C, NULL, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ C, nullptr, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
if (!object_node) {
BKE_report(op->reports, RPT_WARNING, "Could not add node object");
return OPERATOR_CANCELLED;
@@ -462,7 +482,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- bNodeSocketValueObject *socket_data = sock->default_value;
+ bNodeSocketValueObject *socket_data = (bNodeSocketValueObject *)sock->default_value;
socket_data->value = object;
id_us_plus(&object->id);
@@ -473,6 +493,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
snode_dag_update(C, snode);
ED_node_tag_update_nodetree(bmain, ntree, object_node);
+ DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
@@ -533,7 +554,7 @@ static Tex *node_add_texture_get_and_poll_texture_node_tree(Main *bmain, wmOpera
Tex *texture = (Tex *)BKE_libblock_find_name(bmain, ID_TE, name);
if (!texture) {
- return NULL;
+ return nullptr;
}
return texture;
@@ -553,7 +574,7 @@ static int node_add_texture_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
bNode *texture_node = node_add_node(C,
- NULL,
+ nullptr,
GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE,
snode->runtime->cursor[0],
snode->runtime->cursor[1]);
@@ -570,6 +591,7 @@ static int node_add_texture_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
+ DEG_relations_tag_update(bmain);
ED_node_tag_update_nodetree(bmain, ntree, texture_node);
@@ -634,7 +656,7 @@ static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *b
Collection *collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name);
if (!collection) {
- return NULL;
+ return nullptr;
}
return collection;
@@ -654,7 +676,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
bNode *collection_node = node_add_node(
- C, NULL, GEO_NODE_COLLECTION_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ C, nullptr, GEO_NODE_COLLECTION_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
if (!collection_node) {
BKE_report(op->reports, RPT_WARNING, "Could not add node collection");
return OPERATOR_CANCELLED;
@@ -666,7 +688,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- bNodeSocketValueCollection *socket_data = sock->default_value;
+ bNodeSocketValueCollection *socket_data = (bNodeSocketValueCollection *)sock->default_value;
socket_data->value = collection;
id_us_plus(&collection->id);
@@ -675,6 +697,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
+ DEG_relations_tag_update(bmain);
ED_node_tag_update_nodetree(bmain, ntree, collection_node);
@@ -767,7 +790,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- node = node_add_node(C, NULL, type, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ node = node_add_node(C, nullptr, type, snode->runtime->cursor[0], snode->runtime->cursor[1]);
if (!node) {
BKE_report(op->reports, RPT_WARNING, "Could not add an image node");
@@ -780,12 +803,13 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
* to get proper image source.
*/
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
- BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD);
+ BKE_image_signal(bmain, ima, nullptr, IMA_SIGNAL_RELOAD);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
}
snode_notify(C, snode);
snode_dag_update(C, snode);
+ DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
@@ -855,7 +879,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNode *node;
- ID *mask = NULL;
+ ID *mask = nullptr;
/* check input variables */
char name[MAX_ID_NAME - 2];
@@ -869,7 +893,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
node = node_add_node(
- C, NULL, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ C, nullptr, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]);
if (!node) {
BKE_report(op->reports, RPT_WARNING, "Could not add a mask node");
@@ -881,6 +905,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
+ DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
@@ -955,7 +980,7 @@ static int new_node_tree_exec(bContext *C, wmOperator *op)
id_us_min(&ntree->id);
RNA_id_pointer_create(&ntree->id, &idptr);
- RNA_property_pointer_set(&ptr, prop, idptr, NULL);
+ RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
RNA_property_update(C, &ptr, prop);
}
else if (snode) {
@@ -972,7 +997,7 @@ static const EnumPropertyItem *new_node_tree_type_itemf(bContext *UNUSED(C),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
- return rna_node_tree_type_itemf(NULL, NULL, r_free);
+ return rna_node_tree_type_itemf(nullptr, nullptr, r_free);
}
void NODE_OT_new_node_tree(wmOperatorType *ot)
diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c
index eb89658857b..336b0c46a81 100644
--- a/source/blender/editors/space_node/node_buttons.c
+++ b/source/blender/editors/space_node/node_buttons.c
@@ -157,6 +157,11 @@ static void draw_socket_list(const bContext *C,
RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, socket, &socket_ptr);
uiItemR(layout, &socket_ptr, "name", 0, NULL, ICON_NONE);
+ /* Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. */
+ if (ntree->type == NTREE_GEOMETRY) {
+ uiItemR(layout, &socket_ptr, "description", 0, NULL, ICON_NONE);
+ }
+
if (socket->typeinfo->interface_draw) {
socket->typeinfo->interface_draw((bContext *)C, layout, &socket_ptr);
}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index f64ce771b25..1dc8e1412af 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -82,6 +82,7 @@
# include "COM_compositor.h"
#endif
+using blender::Map;
using blender::Set;
using blender::Span;
using blender::Vector;
@@ -1404,6 +1405,28 @@ static void node_draw_basis(const bContext *C,
"");
UI_block_emboss_set(node->block, UI_EMBOSS);
}
+ if (ntree->type == NTREE_GEOMETRY) {
+ /* Active preview toggle. */
+ iconofs -= iconbutw;
+ UI_block_emboss_set(node->block, UI_EMBOSS_NONE);
+ int icon = (node->flag & NODE_ACTIVE_PREVIEW) ? ICON_RESTRICT_VIEW_OFF : ICON_RESTRICT_VIEW_ON;
+ uiBut *but = uiDefIconBut(node->block,
+ UI_BTYPE_BUT_TOGGLE,
+ 0,
+ icon,
+ iconofs,
+ rct->ymax - NODE_DY,
+ iconbutw,
+ UI_UNIT_Y,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "Show this node's geometry output in the spreadsheet");
+ UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_active_preview_toggle");
+ UI_block_emboss_set(node->block, UI_EMBOSS);
+ }
node_add_error_message_button(C, *ntree, *node, *rct, iconofs);
@@ -1745,26 +1768,27 @@ static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
static void count_mutli_input_socket_links(bNodeTree *ntree, SpaceNode *snode)
{
+ Map<bNodeSocket *, int> counts;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (link->tosock->flag & SOCK_MULTI_INPUT) {
+ int &count = counts.lookup_or_add(link->tosock, 0);
+ count++;
+ }
+ }
+ /* Count temporary links going into this socket. */
+ LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) {
+ LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
+ bNodeLink *link = (bNodeLink *)linkdata->data;
+ if (link->tosock && (link->tosock->flag & SOCK_MULTI_INPUT)) {
+ int &count = counts.lookup_or_add(link->tosock, 0);
+ count++;
+ }
+ }
+ }
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- LISTBASE_FOREACH (struct bNodeSocket *, socket, &node->inputs) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
if (socket->flag & SOCK_MULTI_INPUT) {
- Set<bNodeSocket *> visited_from_sockets;
- socket->total_inputs = 0;
- LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
- if (link->tosock == socket) {
- visited_from_sockets.add(link->fromsock);
- }
- }
- /* Count temporary links going into this socket. */
- LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) {
- LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = (bNodeLink *)linkdata->data;
- if (link->tosock == socket) {
- visited_from_sockets.add(link->fromsock);
- }
- }
- }
- socket->total_inputs = visited_from_sockets.size();
+ socket->total_inputs = counts.lookup_default(socket, 0);
}
}
}
@@ -1959,8 +1983,8 @@ void node_draw_space(const bContext *C, ARegion *region)
ID *name_id = (path->nodetree && path->nodetree != snode->nodetree) ? &path->nodetree->id :
snode->id;
- if (name_id && UNLIKELY(!STREQ(path->node_name, name_id->name + 2))) {
- BLI_strncpy(path->node_name, name_id->name + 2, sizeof(path->node_name));
+ if (name_id && UNLIKELY(!STREQ(path->display_name, name_id->name + 2))) {
+ BLI_strncpy(path->display_name, name_id->name + 2, sizeof(path->display_name));
}
/* Current View2D center, will be set temporarily for parent node trees. */
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.cc
index b72a6503749..d86b069aac3 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -41,6 +41,7 @@
#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
+#include "BKE_workspace.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -82,7 +83,7 @@ enum {
COM_RECALC_VIEWER = 2,
};
-typedef struct CompoJob {
+struct CompoJob {
/* Input parameters. */
Main *bmain;
Scene *scene;
@@ -96,7 +97,7 @@ typedef struct CompoJob {
const short *stop;
short *do_update;
float *progress;
-} CompoJob;
+};
float node_socket_calculate_height(const bNodeSocket *socket)
{
@@ -149,7 +150,7 @@ static int compo_get_recalc_flags(const bContext *C)
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_IMAGE) {
- SpaceImage *sima = area->spacedata.first;
+ SpaceImage *sima = (SpaceImage *)area->spacedata.first;
if (sima->image) {
if (sima->image->type == IMA_TYPE_R_RESULT) {
recalc_flags |= COM_RECALC_COMPOSITE;
@@ -160,7 +161,7 @@ static int compo_get_recalc_flags(const bContext *C)
}
}
else if (area->spacetype == SPACE_NODE) {
- SpaceNode *snode = area->spacedata.first;
+ SpaceNode *snode = (SpaceNode *)area->spacedata.first;
if (snode->flag & SNODE_BACKDRAW) {
recalc_flags |= COM_RECALC_VIEWER;
}
@@ -174,7 +175,7 @@ static int compo_get_recalc_flags(const bContext *C)
/* called by compo, only to check job 'stop' value */
static int compo_breakjob(void *cjv)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
/* without G.is_break 'ESC' wont quit - which annoys users */
return (*(cj->stop)
@@ -187,7 +188,7 @@ static int compo_breakjob(void *cjv)
/* called by compo, wmJob sends notifier */
static void compo_statsdrawjob(void *cjv, const char *UNUSED(str))
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
*(cj->do_update) = true;
}
@@ -195,19 +196,19 @@ static void compo_statsdrawjob(void *cjv, const char *UNUSED(str))
/* called by compo, wmJob sends notifier */
static void compo_redrawjob(void *cjv)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
*(cj->do_update) = true;
}
static void compo_freejob(void *cjv)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
if (cj->localtree) {
ntreeLocalMerge(cj->bmain, cj->localtree, cj->ntree);
}
- if (cj->compositor_depsgraph != NULL) {
+ if (cj->compositor_depsgraph != nullptr) {
DEG_graph_free(cj->compositor_depsgraph);
}
MEM_freeN(cj);
@@ -217,7 +218,7 @@ static void compo_freejob(void *cjv)
* sliding buttons doesn't frustrate */
static void compo_initjob(void *cjv)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
Main *bmain = cj->bmain;
Scene *scene = cj->scene;
ViewLayer *view_layer = cj->view_layer;
@@ -242,12 +243,12 @@ static void compo_initjob(void *cjv)
/* called before redraw notifiers, it moves finished previews over */
static void compo_updatejob(void *UNUSED(cjv))
{
- WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, NULL);
+ WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, nullptr);
}
static void compo_progressjob(void *cjv, float progress)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
*(cj->progress) = progress;
}
@@ -260,7 +261,7 @@ static void compo_startjob(void *cjv,
short *do_update,
float *progress)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
bNodeTree *ntree = cj->localtree;
Scene *scene = cj->scene;
@@ -310,9 +311,9 @@ static void compo_startjob(void *cjv,
}
}
- ntree->test_break = NULL;
- ntree->stats_draw = NULL;
- ntree->progress = NULL;
+ ntree->test_break = nullptr;
+ ntree->stats_draw = nullptr;
+ ntree->progress = nullptr;
}
/**
@@ -346,7 +347,7 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene
"Compositing",
WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS,
WM_JOB_TYPE_COMPOSITE);
- CompoJob *cj = MEM_callocN(sizeof(CompoJob), "compo job");
+ CompoJob *cj = (CompoJob *)MEM_callocN(sizeof(CompoJob), "compo job");
/* customdata for preview thread */
cj->bmain = bmain;
@@ -358,7 +359,7 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene
/* setup job */
WM_jobs_customdata_set(wm_job, cj, compo_freejob);
WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_COMPO_RESULT, NC_SCENE | ND_COMPO_RESULT);
- WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, NULL);
+ WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, nullptr);
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
@@ -411,7 +412,7 @@ void snode_notify(bContext *C, SpaceNode *snode)
{
ID *id = snode->id;
- WM_event_add_notifier(C, NC_NODE | NA_EDITED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_EDITED, nullptr);
if (ED_node_is_shader(snode)) {
if (GS(id->name) == ID_MA) {
@@ -489,15 +490,15 @@ void ED_node_shader_default(const bContext *C, ID *id)
}
else if (ELEM(GS(id->name), ID_WO, ID_LA)) {
/* Emission */
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
bNode *shader, *output;
if (GS(id->name) == ID_WO) {
World *world = (World *)id;
world->nodetree = ntree;
- shader = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
- output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
+ shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_BACKGROUND);
+ output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_WORLD);
nodeAddLink(ntree,
shader,
nodeFindSocket(shader, SOCK_OUT, "Background"),
@@ -511,8 +512,8 @@ void ED_node_shader_default(const bContext *C, ID *id)
Light *light = (Light *)id;
light->nodetree = ntree;
- shader = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
- output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_LIGHT);
+ shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION);
+ output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_LIGHT);
nodeAddLink(ntree,
shader,
nodeFindSocket(shader, SOCK_OUT, "Emission"),
@@ -545,7 +546,7 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
return;
}
- sce->nodetree = ntreeAddTree(NULL, "Compositing Nodetree", ntreeType_Composite->idname);
+ sce->nodetree = ntreeAddTree(nullptr, "Compositing Nodetree", ntreeType_Composite->idname);
sce->nodetree->chunksize = 256;
sce->nodetree->edit_quality = NTREE_QUALITY_HIGH;
@@ -561,8 +562,8 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
nodeSetActive(sce->nodetree, in);
/* links from color to color */
- bNodeSocket *fromsock = in->outputs.first;
- bNodeSocket *tosock = out->inputs.first;
+ bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first;
+ bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
ntreeUpdateTree(CTX_data_main(C), sce->nodetree);
@@ -580,7 +581,7 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
return;
}
- tex->nodetree = ntreeAddTree(NULL, "Texture Nodetree", ntreeType_Texture->idname);
+ tex->nodetree = ntreeAddTree(nullptr, "Texture Nodetree", ntreeType_Texture->idname);
bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT);
out->locx = 300.0f;
@@ -591,8 +592,8 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
in->locy = 300.0f;
nodeSetActive(tex->nodetree, in);
- bNodeSocket *fromsock = in->outputs.first;
- bNodeSocket *tosock = out->inputs.first;
+ bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first;
+ bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
nodeAddLink(tex->nodetree, in, fromsock, out, tosock);
ntreeUpdateTree(CTX_data_main(C), tex->nodetree);
@@ -617,24 +618,24 @@ void snode_set_context(const bContext *C)
if (snode->nodetree && !STREQ(snode->nodetree->idname, snode->tree_idname)) {
/* current tree does not match selected type, clear tree path */
- ntree = NULL;
- id = NULL;
- from = NULL;
+ ntree = nullptr;
+ id = nullptr;
+ from = nullptr;
}
- if (!(snode->flag & SNODE_PIN) || ntree == NULL) {
+ if (!(snode->flag & SNODE_PIN) || ntree == nullptr) {
if (treetype->get_from_context) {
/* reset and update from context */
- ntree = NULL;
- id = NULL;
- from = NULL;
+ ntree = nullptr;
+ id = nullptr;
+ from = nullptr;
treetype->get_from_context(C, treetype, &ntree, &id, &from);
}
}
if (snode->nodetree != ntree || snode->id != id || snode->from != from ||
- (snode->treepath.last == NULL && ntree)) {
+ (snode->treepath.last == nullptr && ntree)) {
ED_node_tree_start(snode, ntree, id, from);
}
}
@@ -647,7 +648,7 @@ void snode_update(SpaceNode *snode, bNode *node)
*/
/* update all edited group nodes */
- bNodeTreePath *path = snode->treepath.last;
+ bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last;
if (path) {
bNodeTree *ngroup = path->nodetree;
for (path = path->prev; path; path = path->prev) {
@@ -684,7 +685,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
node->flag |= NODE_DO_OUTPUT;
if (!was_output) {
- do_update = 1;
+ do_update = true;
}
}
@@ -733,7 +734,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
*r_active_texture_changed = true;
}
ED_node_tag_update_nodetree(bmain, ntree, node);
- WM_main_add_notifier(NC_IMAGE, NULL);
+ WM_main_add_notifier(NC_IMAGE, nullptr);
}
WM_main_add_notifier(NC_MATERIAL | ND_NODES, node->id);
@@ -752,7 +753,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
ED_node_tag_update_nodetree(bmain, ntree, node);
}
- /* addnode() doesn't link this yet... */
+ /* Adding a node doesn't link this yet. */
node->id = (ID *)BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
}
else if (node->type == CMP_NODE_COMPOSITE) {
@@ -805,7 +806,7 @@ static bool edit_node_poll(bContext *C)
static void edit_node_properties(wmOperatorType *ot)
{
/* XXX could node be a context pointer? */
- RNA_def_string(ot->srna, "node", NULL, MAX_NAME, "Node", "");
+ RNA_def_string(ot->srna, "node", nullptr, MAX_NAME, "Node", "");
RNA_def_int(ot->srna, "socket", 0, 0, MAX_SOCKET, "Socket", "", 0, MAX_SOCKET);
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Side", "");
}
@@ -837,7 +838,7 @@ static void edit_node_properties_get(
wmOperator *op, bNodeTree *ntree, bNode **r_node, bNodeSocket **r_sock, int *r_in_out)
{
bNode *node;
- bNodeSocket *sock = NULL;
+ bNodeSocket *sock = nullptr;
char nodename[MAX_NAME];
int sockindex;
int in_out;
@@ -875,29 +876,30 @@ static void edit_node_properties_get(
static bNode *visible_node(SpaceNode *snode, const rctf *rct)
{
LISTBASE_FOREACH_BACKWARD (bNode *, node, &snode->edittree->nodes) {
- if (BLI_rctf_isect(&node->totr, rct, NULL)) {
+ if (BLI_rctf_isect(&node->totr, rct, nullptr)) {
return node;
}
}
- return NULL;
+ return nullptr;
}
/* ********************** size widget operator ******************** */
-typedef struct NodeSizeWidget {
+struct NodeSizeWidget {
float mxstart, mystart;
float oldlocx, oldlocy;
float oldoffsetx, oldoffsety;
float oldwidth, oldheight;
int directions;
-} NodeSizeWidget;
+};
static void node_resize_init(
bContext *C, wmOperator *op, const wmEvent *UNUSED(event), bNode *node, int dir)
{
SpaceNode *snode = CTX_wm_space_node(C);
- NodeSizeWidget *nsw = MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
+ NodeSizeWidget *nsw = (NodeSizeWidget *)MEM_callocN(sizeof(NodeSizeWidget),
+ "size widget op data");
op->customdata = nsw;
nsw->mxstart = snode->runtime->cursor[0] * UI_DPI_FAC;
@@ -925,7 +927,7 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel)
if (cancel) {
SpaceNode *snode = CTX_wm_space_node(C);
bNode *node = nodeGetActive(snode->edittree);
- NodeSizeWidget *nsw = op->customdata;
+ NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata;
node->locx = nsw->oldlocx;
node->locy = nsw->oldlocy;
@@ -936,7 +938,7 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel)
}
MEM_freeN(op->customdata);
- op->customdata = NULL;
+ op->customdata = nullptr;
}
static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
@@ -944,7 +946,7 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
SpaceNode *snode = CTX_wm_space_node(C);
ARegion *region = CTX_wm_region(C);
bNode *node = nodeGetActive(snode->edittree);
- NodeSizeWidget *nsw = op->customdata;
+ NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata;
switch (event->type) {
case MOUSEMOVE: {
@@ -1111,7 +1113,7 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
else {
/* hide unused sockets */
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- if (sock->link == NULL) {
+ if (sock->link == nullptr) {
sock->flag |= SOCK_HIDDEN;
}
}
@@ -1127,12 +1129,17 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket *socket)
{
const float node_socket_height = node_socket_calculate_height(socket);
- const rctf multi_socket_rect = {
- .xmin = socket->locx - NODE_SOCKSIZE * 4,
- .xmax = socket->locx + NODE_SOCKSIZE,
- .ymin = socket->locy - node_socket_height * 0.5 - NODE_SOCKSIZE * 2.0f,
- .ymax = socket->locy + node_socket_height * 0.5 + NODE_SOCKSIZE * 2.0f,
- };
+ rctf multi_socket_rect;
+ /*.xmax = socket->locx + NODE_SOCKSIZE * 5.5f
+ * would be the same behavior as for regular sockets.
+ * But keep it smaller because for multi-input socket you
+ * sometimes want to drag the link to the other side, if you may
+ * accidentally pick the wrong link otherwise. */
+ BLI_rctf_init(&multi_socket_rect,
+ socket->locx - NODE_SOCKSIZE * 4.0f,
+ socket->locx + NODE_SOCKSIZE * 2.0f,
+ socket->locy - node_socket_height,
+ socket->locy + node_socket_height);
if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) {
return true;
}
@@ -1141,12 +1148,12 @@ static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSo
/* type is SOCK_IN and/or SOCK_OUT */
int node_find_indicated_socket(
- SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out)
+ SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, const float cursor[2], int in_out)
{
rctf rect;
- *nodep = NULL;
- *sockp = NULL;
+ *nodep = nullptr;
+ *sockp = nullptr;
/* check if we click in a socket */
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
@@ -1238,7 +1245,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- bNode *lastnode = ntree->nodes.last;
+ bNode *lastnode = (bNode *)ntree->nodes.last;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
@@ -1256,14 +1263,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!
*/
- bNodeLink *lastlink = ntree->links.last;
+ bNodeLink *lastlink = (bNodeLink *)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 keep_inputs is set, also copies input links from unselected (when fromnode==nullptr)!
*/
if (link->tonode && (link->tonode->flag & NODE_SELECT) &&
(keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) {
- bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
+ bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
@@ -1311,6 +1318,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
nodeSetSelected(node, false);
node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE);
nodeSetSelected(newnode, true);
+ newnode->flag &= ~NODE_ACTIVE_PREVIEW;
do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, newnode));
}
@@ -1346,7 +1354,7 @@ void NODE_OT_duplicate(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(
- ot->srna, "keep_inputs", 0, "Keep Inputs", "Keep the input links to duplicated nodes");
+ ot->srna, "keep_inputs", false, "Keep Inputs", "Keep the input links to duplicated nodes");
}
bool ED_node_select_check(ListBase *lb)
@@ -1407,7 +1415,7 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
if ((node->type == CMP_NODE_R_LAYERS) ||
(node->type == CMP_NODE_CRYPTOMATTE && node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER)) {
ID *id = node->id;
- if (id == NULL) {
+ if (id == nullptr) {
continue;
}
if (id->tag & LIB_TAG_DOIT) {
@@ -1446,7 +1454,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
/* 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. */
- bNode *node = NULL;
+ bNode *node = nullptr;
LISTBASE_FOREACH (bNode *, node_iter, &sce->nodetree->nodes) {
if (node_iter->id == (ID *)sce) {
node = node_iter;
@@ -1455,7 +1463,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
}
if (node) {
- ViewLayer *view_layer = BLI_findlink(&sce->view_layers, node->custom1);
+ ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, node->custom1);
if (view_layer) {
PointerRNA op_ptr;
@@ -1464,7 +1472,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
RNA_string_set(&op_ptr, "layer", view_layer->name);
RNA_string_set(&op_ptr, "scene", sce->id.name + 2);
- /* to keep keypositions */
+ /* To keep keyframe positions. */
sce->r.scemode |= R_NO_FRAME_UPDATE;
WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr);
@@ -1547,13 +1555,13 @@ static int node_hide_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode *snode = CTX_wm_space_node(C);
/* sanity checking (poll callback checks this already) */
- if ((snode == NULL) || (snode->edittree == NULL)) {
+ if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
node_flag_toggle_exec(snode, NODE_HIDDEN);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1578,7 +1586,7 @@ static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode *snode = CTX_wm_space_node(C);
/* sanity checking (poll callback checks this already) */
- if ((snode == NULL) || (snode->edittree == NULL)) {
+ if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
@@ -1611,13 +1619,13 @@ static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode *snode = CTX_wm_space_node(C);
/* sanity checking (poll callback checks this already) */
- if ((snode == NULL) || (snode->edittree == NULL)) {
+ if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
node_flag_toggle_exec(snode, NODE_OPTIONS);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1642,7 +1650,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode *snode = CTX_wm_space_node(C);
/* sanity checking (poll callback checks this already) */
- if ((snode == NULL) || (snode->edittree == NULL)) {
+ if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
@@ -1667,7 +1675,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1706,8 +1714,6 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
snode_notify(C, snode);
if (do_tag_update) {
snode_dag_update(C, snode);
@@ -1748,8 +1754,6 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
@@ -1867,12 +1871,12 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNodeTree *ntree = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree = nullptr;
+ bNode *node = nullptr;
char file_path[MAX_NAME];
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
@@ -1916,11 +1920,11 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *U
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNodeTree *ntree = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree = nullptr;
+ bNode *node = nullptr;
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
@@ -1962,10 +1966,10 @@ 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;
+ bNode *node = nullptr;
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
}
else if (snode && snode->edittree) {
node = nodeGetActive(snode->edittree);
@@ -1975,9 +1979,9 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- NodeImageMultiFile *nimf = node->storage;
+ NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
- bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input);
+ bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input);
if (!sock) {
return OPERATOR_CANCELLED;
}
@@ -2011,7 +2015,7 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
void NODE_OT_output_file_move_active_socket(wmOperatorType *ot)
{
static const EnumPropertyItem direction_items[] = {
- {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, NULL, 0, NULL, NULL}};
+ {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, nullptr, 0, nullptr, nullptr}};
/* identifiers */
ot->name = "Move File Node Socket";
@@ -2056,7 +2060,7 @@ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2094,7 +2098,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
/* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */
bNode *new_node = BKE_node_copy_store_new_pointers(
- NULL, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN);
+ nullptr, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN);
BKE_node_clipboard_add_node(new_node);
}
}
@@ -2124,7 +2128,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
/* This creates new links between copied nodes. */
if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode &&
(link->fromnode->flag & NODE_SELECT)) {
- bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
+ bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
@@ -2185,13 +2189,25 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
/* make sure all clipboard nodes would be valid in the target tree */
bool all_nodes_valid = true;
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
- if (!node->typeinfo->poll_instance || !node->typeinfo->poll_instance(node, ntree)) {
+ const char *disabled_hint = nullptr;
+ if (!node->typeinfo->poll_instance ||
+ !node->typeinfo->poll_instance(node, ntree, &disabled_hint)) {
all_nodes_valid = false;
- BKE_reportf(op->reports,
- RPT_ERROR,
- "Cannot add node %s into node tree %s",
- node->name,
- ntree->id.name + 2);
+ if (disabled_hint) {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Cannot add node %s into node tree %s:\n %s",
+ node->name,
+ ntree->id.name + 2,
+ disabled_hint);
+ }
+ else {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Cannot add node %s into node tree %s",
+ node->name,
+ ntree->id.name + 2);
+ }
}
}
if (!all_nodes_valid) {
@@ -2271,7 +2287,7 @@ static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb)
return socket;
}
}
- return NULL;
+ return nullptr;
}
static int ntree_socket_add_exec(bContext *C, wmOperator *op)
@@ -2282,7 +2298,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
PointerRNA ntree_ptr;
RNA_id_pointer_create((ID *)ntree, &ntree_ptr);
- const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
+ const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs;
const char *default_name = (in_out == SOCK_IN) ? "Input" : "Output";
@@ -2313,7 +2329,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2341,11 +2357,11 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
+ const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs :
&ntree->outputs);
- if (iosock == NULL) {
+ if (iosock == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -2363,7 +2379,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2389,7 +2405,7 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot)
static const EnumPropertyItem move_direction_items[] = {
{1, "UP", 0, "Up", ""},
{2, "DOWN", 0, "Down", ""},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static int ntree_socket_move_exec(bContext *C, wmOperator *op)
@@ -2398,12 +2414,12 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op)
bNodeTree *ntree = snode->edittree;
int direction = RNA_enum_get(op->ptr, "direction");
- const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
+ const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs;
bNodeSocket *iosock = ntree_get_active_interface_socket(sockets);
- if (iosock == NULL) {
+ if (iosock == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -2438,7 +2454,7 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2471,18 +2487,18 @@ static bool node_shader_script_update_poll(bContext *C)
/* test if we have a render engine that supports shaders scripts */
if (!(type && type->update_script_node)) {
- return 0;
+ return false;
}
/* see if we have a shader script node in context */
- bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
+ bNode *node = (bNode *)CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
if (!node && snode && snode->edittree) {
node = nodeGetActive(snode->edittree);
}
if (node && node->type == SH_NODE_SCRIPT) {
- NodeShaderScript *nss = node->storage;
+ NodeShaderScript *nss = (NodeShaderScript *)node->storage;
if (node->id || nss->filepath[0]) {
return ED_operator_node_editable(C);
@@ -2490,14 +2506,14 @@ static bool node_shader_script_update_poll(bContext *C)
}
/* see if we have a text datablock in context */
- Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
+ Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
if (text) {
- return 1;
+ return true;
}
/* we don't check if text datablock is actually in use, too slow for poll */
- return 0;
+ return false;
}
/* recursively check for script nodes in groups using this text and update */
@@ -2541,11 +2557,11 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
engine->reports = op->reports;
/* get node */
- bNodeTree *ntree_base = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree_base = nullptr;
+ bNode *node = nullptr;
if (nodeptr.data) {
ntree_base = (bNodeTree *)nodeptr.owner_id;
- node = nodeptr.data;
+ node = (bNode *)nodeptr.data;
}
else if (snode && snode->edittree) {
ntree_base = snode->edittree;
@@ -2560,7 +2576,7 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
}
else {
/* update all nodes using text datablock */
- Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
+ Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
if (text) {
/* clear flags for recursion check */
@@ -2632,7 +2648,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (ibuf) {
ARegion *region = CTX_wm_region(C);
@@ -2668,7 +2684,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op)
}
snode_notify(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
}
else {
btree->flag &= ~NTREE_VIEWER_BORDER;
@@ -2708,7 +2724,7 @@ static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op))
btree->flag &= ~NTREE_VIEWER_BORDER;
snode_notify(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2734,11 +2750,11 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNodeTree *ntree = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree = nullptr;
+ bNode *node = nullptr;
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
@@ -2778,11 +2794,11 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(o
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNodeTree *ntree = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree = nullptr;
+ bNode *node = nullptr;
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index 6d0cd254505..94080a7b616 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -30,6 +30,13 @@
#include "BKE_node_ui_storage.hh"
#include "BKE_object.h"
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+
+#include "ED_undo.h"
+
+#include "BLT_translation.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -37,44 +44,71 @@
using blender::IndexRange;
using blender::Map;
-using blender::MultiValueMap;
using blender::Set;
using blender::StringRef;
struct AttributeSearchData {
- const bNodeTree &node_tree;
- const bNode &node;
+ AvailableAttributeInfo &dummy_info_for_search;
+ const NodeUIStorage &ui_storage;
+ bNodeSocket &socket;
+};
- uiBut *search_button;
+/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */
+BLI_STATIC_ASSERT(std::is_trivially_destructible_v<AttributeSearchData>, "");
- /* Used to keep track of a button pointer over multiple redraws. Since the UI code
- * may reallocate the button, without this we might end up with a dangling pointer. */
- uiButStore *button_store;
- uiBlock *button_store_block;
-};
+static StringRef attribute_data_type_string(const CustomDataType type)
+{
+ const char *name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name);
+ return StringRef(IFACE_(name));
+}
+
+static StringRef attribute_domain_string(const AttributeDomain domain)
+{
+ const char *name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name);
+ return StringRef(IFACE_(name));
+}
+
+/* Unicode arrow. */
+#define MENU_SEP "\xe2\x96\xb6"
+
+static bool attribute_search_item_add(uiSearchItems *items, const AvailableAttributeInfo &item)
+{
+ const StringRef data_type_name = attribute_data_type_string(item.data_type);
+ const StringRef domain_name = attribute_domain_string(item.domain);
+ std::string search_item_text = domain_name + " " + MENU_SEP + item.name + UI_SEP_CHAR +
+ data_type_name;
+
+ return UI_search_item_add(
+ items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0);
+}
-static void attribute_search_update_fn(
- const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
+static void attribute_search_update_fn(const bContext *UNUSED(C),
+ void *arg,
+ const char *str,
+ uiSearchItems *items,
+ const bool is_first)
{
AttributeSearchData *data = static_cast<AttributeSearchData *>(arg);
- const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context(
- C, data->node_tree, data->node);
- if (ui_storage == nullptr) {
- return;
- }
- const MultiValueMap<std::string, AvailableAttributeInfo> &attribute_hints =
- ui_storage->attribute_hints;
+ const Set<AvailableAttributeInfo> &attribute_hints = data->ui_storage.attribute_hints;
- if (str[0] != '\0' && attribute_hints.lookup_as(StringRef(str)).is_empty()) {
- /* Any string may be valid, so add the current search string with the hints. */
- UI_search_item_add(items, str, (void *)str, ICON_ADD, 0, 0);
+ /* Any string may be valid, so add the current search string along with the hints. */
+ if (str[0] != '\0') {
+ /* Note that the attribute domain and data type are dummies, since
+ * #AvailableAttributeInfo equality is only based on the string. */
+ if (!attribute_hints.contains(AvailableAttributeInfo{str, ATTR_DOMAIN_AUTO, CD_PROP_BOOL})) {
+ data->dummy_info_for_search.name = std::string(str);
+ UI_search_item_add(items, str, &data->dummy_info_for_search, ICON_ADD, 0, 0);
+ }
}
if (str[0] == '\0' && !is_first) {
/* Allow clearing the text field when the string is empty, but not on the first pass,
* or opening an attribute field for the first time would show this search item. */
- UI_search_item_add(items, str, (void *)str, ICON_X, 0, 0);
+ data->dummy_info_for_search.name = std::string(str);
+ UI_search_item_add(items, str, &data->dummy_info_for_search, ICON_X, 0, 0);
}
/* Don't filter when the menu is first opened, but still run the search
@@ -82,16 +116,16 @@ static void attribute_search_update_fn(
const char *string = is_first ? "" : str;
StringSearch *search = BLI_string_search_new();
- for (const std::string &attribute_name : attribute_hints.keys()) {
- BLI_string_search_add(search, attribute_name.c_str(), (void *)&attribute_name);
+ for (const AvailableAttributeInfo &item : attribute_hints) {
+ BLI_string_search_add(search, item.name.c_str(), (void *)&item);
}
- std::string **filtered_items;
+ AvailableAttributeInfo **filtered_items;
const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items);
for (const int i : IndexRange(filtered_amount)) {
- std::string *item = filtered_items[i];
- if (!UI_search_item_add(items, item->c_str(), item, ICON_NONE, 0, 0)) {
+ const AvailableAttributeInfo *item = filtered_items[i];
+ if (!attribute_search_item_add(items, *item)) {
break;
}
}
@@ -100,19 +134,34 @@ static void attribute_search_update_fn(
BLI_string_search_free(search);
}
-static void attribute_search_free_fn(void *arg)
+static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
{
- AttributeSearchData *data = static_cast<AttributeSearchData *>(arg);
+ AttributeSearchData *data = static_cast<AttributeSearchData *>(data_v);
+ AvailableAttributeInfo *item = static_cast<AvailableAttributeInfo *>(item_v);
- UI_butstore_free(data->button_store_block, data->button_store);
- delete data;
+ bNodeSocket &socket = data->socket;
+ bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value);
+ BLI_strncpy(value->value, item->name.c_str(), MAX_NAME);
+
+ ED_undo_push(C, "Assign Attribute Name");
}
-void node_geometry_add_attribute_search_button(const bNodeTree *node_tree,
+void node_geometry_add_attribute_search_button(const bContext *C,
+ const bNodeTree *node_tree,
const bNode *node,
PointerRNA *socket_ptr,
uiLayout *layout)
{
+ const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context(
+ C, *node_tree, *node);
+
+ if (ui_storage == nullptr) {
+ uiItemR(layout, socket_ptr, "default_value", 0, "", 0);
+ return;
+ }
+
+ const NodeTreeUIStorage *tree_ui_storage = node_tree->ui_storage;
+
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but = uiDefIconTextButR(block,
UI_BTYPE_SEARCH_MENU,
@@ -132,22 +181,19 @@ void node_geometry_add_attribute_search_button(const bNodeTree *node_tree,
0.0f,
"");
- AttributeSearchData *data = new AttributeSearchData{
- *node_tree,
- *node,
- but,
- UI_butstore_create(block),
- block,
- };
-
- UI_butstore_register(data->button_store, &data->search_button);
+ AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData,
+ {tree_ui_storage->dummy_info_for_search,
+ *ui_storage,
+ *static_cast<bNodeSocket *>(socket_ptr->data)});
UI_but_func_search_set_results_are_suggestions(but, true);
+ UI_but_func_search_set_sep_string(but, MENU_SEP);
UI_but_func_search_set(but,
nullptr,
attribute_search_update_fn,
static_cast<void *>(data),
- attribute_search_free_fn,
+ true,
nullptr,
+ attribute_search_exec_fn,
nullptr);
}
diff --git a/source/blender/editors/space_node/node_gizmo.c b/source/blender/editors/space_node/node_gizmo.c
index 28d7e1b8d04..8547c825230 100644
--- a/source/blender/editors/space_node/node_gizmo.c
+++ b/source/blender/editors/space_node/node_gizmo.c
@@ -97,7 +97,6 @@ static void gizmo_node_backdrop_prop_matrix_set(const wmGizmo *UNUSED(gz),
BLI_assert(gz_prop->type->array_length == 16);
SpaceNode *snode = gz_prop->custom_func.user_data;
snode->zoom = matrix[0][0];
- snode->zoom = matrix[1][1];
snode->xof = matrix[3][0];
snode->yof = matrix[3][1];
}
diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.cc
index e1de4bfc21e..851d386ad0d 100644
--- a/source/blender/editors/space_node/node_group.c
+++ b/source/blender/editors/space_node/node_group.cc
@@ -21,7 +21,7 @@
* \ingroup spnode
*/
-#include <stdlib.h>
+#include <cstdlib>
#include "MEM_guardedalloc.h"
@@ -70,9 +70,8 @@ static bool node_group_operator_active_poll(bContext *C)
SpaceNode *snode = CTX_wm_space_node(C);
/* Group operators only defined for standard node tree types.
- * Disabled otherwise to allow pynodes define their own operators
- * with same keymap.
- */
+ * Disabled otherwise to allow python-nodes define their own operators
+ * with same key-map. */
if (STR_ELEM(snode->tree_idname,
"ShaderNodeTree",
"CompositorNodeTree",
@@ -90,9 +89,8 @@ static bool node_group_operator_editable(bContext *C)
SpaceNode *snode = CTX_wm_space_node(C);
/* Group operators only defined for standard node tree types.
- * Disabled otherwise to allow pynodes define their own operators
- * with same keymap.
- */
+ * Disabled otherwise to allow python-nodes define their own operators
+ * with same key-map. */
if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
ED_node_is_geometry(snode)) {
return true;
@@ -135,7 +133,7 @@ static bNode *node_group_get_active(bContext *C, const char *node_idname)
if (node && STREQ(node->idname, node_idname)) {
return node;
}
- return NULL;
+ return nullptr;
}
/** \} */
@@ -165,7 +163,7 @@ static int node_group_edit_exec(bContext *C, wmOperator *op)
ED_node_tree_pop(snode);
}
- WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_NODES, nullptr);
return OPERATOR_FINISHED;
}
@@ -200,7 +198,8 @@ void NODE_OT_group_edit(wmOperatorType *ot)
static AnimationBasePathChange *animation_basepath_change_new(const char *src_basepath,
const char *dst_basepath)
{
- AnimationBasePathChange *basepath_change = MEM_callocN(sizeof(*basepath_change), AT);
+ AnimationBasePathChange *basepath_change = (AnimationBasePathChange *)MEM_callocN(
+ sizeof(*basepath_change), AT);
basepath_change->src_basepath = src_basepath;
basepath_change->dst_basepath = dst_basepath;
return basepath_change;
@@ -218,13 +217,13 @@ static void animation_basepath_change_free(AnimationBasePathChange *basepath_cha
/* returns 1 if its OK */
static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
{
- /* clear new pointers, set in copytree */
+ /* Clear new pointers, set in #ntreeCopyTree_ex_new_pointers. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- node->new_node = NULL;
+ node->new_node = nullptr;
}
- ListBase anim_basepaths = {NULL, NULL};
- LinkNode *nodes_delayed_free = NULL;
+ ListBase anim_basepaths = {nullptr, nullptr};
+ LinkNode *nodes_delayed_free = nullptr;
bNodeTree *ngroup = (bNodeTree *)gnode->id;
/* wgroup is a temporary copy of the NodeTree we're merging in
@@ -247,7 +246,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
/* keep track of this node's RNA "base" path (the part of the path identifying the node)
* if the old nodetree has animation data which potentially covers this node
*/
- const char *old_animation_basepath = NULL;
+ const char *old_animation_basepath = nullptr;
if (wgroup->adt) {
PointerRNA ptr;
RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr);
@@ -277,7 +276,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
node->flag |= NODE_SELECT;
}
- bNodeLink *glinks_first = ntree->links.last;
+ bNodeLink *glinks_first = (bNodeLink *)ntree->links.last;
/* Add internal links to the ntree */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) {
@@ -285,10 +284,10 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
BLI_addtail(&ntree->links, link);
}
- bNodeLink *glinks_last = ntree->links.last;
+ bNodeLink *glinks_last = (bNodeLink *)ntree->links.last;
/* and copy across the animation,
- * note that the animation data's action can be NULL here */
+ * note that the animation data's action can be nullptr here */
if (wgroup->adt) {
bAction *waction;
@@ -307,7 +306,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
/* free temp action too */
if (waction) {
BKE_id_free(bmain, waction);
- wgroup->adt->action = NULL;
+ wgroup->adt->action = nullptr;
}
}
@@ -317,14 +316,14 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
/* restore external links to and from the gnode */
/* input links */
- if (glinks_first != NULL) {
+ if (glinks_first != nullptr) {
for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) {
if (link->fromnode->type == NODE_GROUP_INPUT) {
const char *identifier = link->fromsock->identifier;
int num_external_links = 0;
/* find external links to this input */
- for (bNodeLink *tlink = ntree->links.first; tlink != glinks_first->next;
+ for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next;
tlink = tlink->next) {
if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) {
nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
@@ -348,10 +347,11 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
}
/* Also iterate over new links to cover passthrough links. */
- glinks_last = ntree->links.last;
+ glinks_last = (bNodeLink *)ntree->links.last;
/* output links */
- for (bNodeLink *link = ntree->links.first; link != glinks_first->next; link = link->next) {
+ for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next;
+ link = link->next) {
if (link->fromnode == gnode) {
const char *identifier = link->fromsock->identifier;
int num_internal_links = 0;
@@ -384,7 +384,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
}
while (nodes_delayed_free) {
- bNode *node = BLI_linklist_pop(&nodes_delayed_free);
+ bNode *node = (bNode *)BLI_linklist_pop(&nodes_delayed_free);
nodeRemoveNode(bmain, ntree, node, false);
}
@@ -455,10 +455,10 @@ static int node_group_separate_selected(
/* clear new pointers, set in BKE_node_copy_ex(). */
LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
- node->new_node = NULL;
+ node->new_node = nullptr;
}
- ListBase anim_basepaths = {NULL, NULL};
+ ListBase anim_basepaths = {nullptr, nullptr};
/* add selected nodes into the ntree */
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup->nodes) {
@@ -543,7 +543,7 @@ static int node_group_separate_selected(
}
/* and copy across the animation,
- * note that the animation data's action can be NULL here */
+ * note that the animation data's action can be nullptr here */
if (ngroup->adt) {
/* now perform the moving */
BKE_animdata_transfer_by_basepath(bmain, &ngroup->id, &ntree->id, &anim_basepaths);
@@ -562,16 +562,16 @@ static int node_group_separate_selected(
return 1;
}
-typedef enum eNodeGroupSeparateType {
+enum eNodeGroupSeparateType {
NODE_GS_COPY,
NODE_GS_MOVE,
-} eNodeGroupSeparateType;
+};
/* Operator Property */
static const EnumPropertyItem node_group_separate_types[] = {
{NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
{NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static int node_group_separate_exec(bContext *C, wmOperator *op)
@@ -628,8 +628,8 @@ static int node_group_separate_invoke(bContext *C,
uiLayout *layout = UI_popup_menu_layout(pup);
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
- uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_COPY);
- uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_MOVE);
+ uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_COPY);
+ uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_MOVE);
UI_popup_menu_end(C, pup);
@@ -674,13 +674,24 @@ static bool node_group_make_test_selected(bNodeTree *ntree,
int ok = true;
/* make a local pseudo node tree to pass to the node poll functions */
- bNodeTree *ngroup = ntreeAddTree(NULL, "Pseudo Node Group", ntree_idname);
+ bNodeTree *ngroup = ntreeAddTree(nullptr, "Pseudo Node Group", ntree_idname);
/* check poll functions for selected nodes */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node_group_make_use_node(node, gnode)) {
- if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, ngroup)) {
- BKE_reportf(reports, RPT_WARNING, "Can not add node '%s' in a group", node->name);
+ const char *disabled_hint = nullptr;
+ if (node->typeinfo->poll_instance &&
+ !node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) {
+ if (disabled_hint) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Can not add node '%s' in a group:\n %s",
+ node->name,
+ disabled_hint);
+ }
+ else {
+ BKE_reportf(reports, RPT_WARNING, "Can not add node '%s' in a group", node->name);
+ }
ok = false;
break;
}
@@ -770,7 +781,7 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree,
expose_visible = true;
}
- ListBase anim_basepaths = {NULL, NULL};
+ ListBase anim_basepaths = {nullptr, nullptr};
/* move nodes over */
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
@@ -984,10 +995,10 @@ static bNode *node_group_make_from_selected(const bContext *C,
Main *bmain = CTX_data_main(C);
float min[2], max[2];
- const int totselect = node_get_selected_minmax(ntree, NULL, min, max, false);
+ const int totselect = node_get_selected_minmax(ntree, nullptr, min, max, false);
/* don't make empty group */
if (totselect == 0) {
- return NULL;
+ return nullptr;
}
/* new nodetree */
@@ -1018,7 +1029,7 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- if (!node_group_make_test_selected(ntree, NULL, ntree_idname, op->reports)) {
+ if (!node_group_make_test_selected(ntree, nullptr, ntree_idname, op->reports)) {
return OPERATOR_CANCELLED;
}
@@ -1031,7 +1042,7 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
if (ngroup) {
ED_node_tree_push(snode, ngroup, gnode);
LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
- sort_multi_input_socket_links(snode, node, NULL, NULL);
+ sort_multi_input_socket_links(snode, node, nullptr, nullptr);
}
ntreeUpdateTree(bmain, ngroup);
}
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 21a36ff9683..2fcc59cde0b 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -58,9 +58,11 @@ typedef struct bNodeLinkDrag {
bool from_multi_input_socket;
int in_out;
- /** Temporarily stores the last picked link from multi input socket operator. */
+ /** Temporarily stores the last picked link from multi-input socket operator. */
struct bNodeLink *last_picked_multi_input_socket_link;
+ /** Temporarily stores the last hovered socket for multi-input socket operator.
+ * Store it to recalculate sorting after it is no longer hovered. */
struct bNode *last_node_hovered_while_dragging_a_link;
} bNodeLinkDrag;
@@ -260,7 +262,7 @@ int node_render_changed_exec(bContext *, struct wmOperator *);
int node_find_indicated_socket(struct SpaceNode *snode,
struct bNode **nodep,
struct bNodeSocket **sockp,
- float cursor[2],
+ const float cursor[2],
int in_out);
void NODE_OT_duplicate(struct wmOperatorType *ot);
@@ -273,6 +275,7 @@ void NODE_OT_hide_toggle(struct wmOperatorType *ot);
void NODE_OT_hide_socket_toggle(struct wmOperatorType *ot);
void NODE_OT_preview_toggle(struct wmOperatorType *ot);
void NODE_OT_options_toggle(struct wmOperatorType *ot);
+void NODE_OT_active_preview_toggle(struct wmOperatorType *ot);
void NODE_OT_node_copy_color(struct wmOperatorType *ot);
void NODE_OT_read_viewlayers(struct wmOperatorType *ot);
@@ -307,7 +310,8 @@ void NODE_OT_cryptomatte_layer_add(struct wmOperatorType *ot);
void NODE_OT_cryptomatte_layer_remove(struct wmOperatorType *ot);
/* node_geometry_attribute_search.cc */
-void node_geometry_add_attribute_search_button(const struct bNodeTree *node_tree,
+void node_geometry_add_attribute_search_button(const struct bContext *C,
+ const struct bNodeTree *node_tree,
const struct bNode *node,
struct PointerRNA *socket_ptr,
struct uiLayout *layout);
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.cc
index 4b2290c094b..57dc0b6fef2 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -55,12 +55,14 @@
#include "node_intern.h" /* own include */
-/* ****************** Relations helpers *********************** */
+/* -------------------------------------------------------------------- */
+/** \name Relations Helpers
+ * \{ */
static bool ntree_has_drivers(bNodeTree *ntree)
{
const AnimData *adt = BKE_animdata_from_id(&ntree->id);
- if (adt == NULL) {
+ if (adt == nullptr) {
return false;
}
return !BLI_listbase_is_empty(&adt->drivers);
@@ -102,7 +104,7 @@ static bool node_group_has_output_dfs(bNode *node)
return false;
}
ntree->id.tag |= LIB_TAG_DOIT;
- for (bNode *current_node = ntree->nodes.first; current_node != NULL;
+ for (bNode *current_node = (bNode *)ntree->nodes.first; current_node != nullptr;
current_node = current_node->next) {
if (current_node->type == NODE_GROUP) {
if (current_node->id && node_group_has_output_dfs(current_node)) {
@@ -120,7 +122,7 @@ static bool node_group_has_output(Main *bmain, bNode *node)
{
BLI_assert(ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP));
bNodeTree *ntree = (bNodeTree *)node->id;
- if (ntree == NULL) {
+ if (ntree == nullptr) {
return false;
}
BKE_main_id_tag_listbase(&bmain->nodetrees, LIB_TAG_DOIT, false);
@@ -145,7 +147,7 @@ bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node)
* is connected to and so eventually.
*/
if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
- if (current_node->id != NULL && ntree_has_drivers((bNodeTree *)current_node->id)) {
+ if (current_node->id != nullptr && ntree_has_drivers((bNodeTree *)current_node->id)) {
return true;
}
if (ntree_check_nodes_connected(ntree, node, current_node) &&
@@ -162,14 +164,18 @@ bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node)
return false;
}
-/* ****************** Add *********************** */
+/** \} */
-typedef struct bNodeListItem {
+/* -------------------------------------------------------------------- */
+/** \name Add Node
+ * \{ */
+
+struct bNodeListItem {
struct bNodeListItem *next, *prev;
struct bNode *node;
-} bNodeListItem;
+};
-typedef struct NodeInsertOfsData {
+struct NodeInsertOfsData {
bNodeTree *ntree;
bNode *insert; /* inserted node */
bNode *prev, *next; /* prev/next node in the chain */
@@ -178,7 +184,7 @@ typedef struct NodeInsertOfsData {
wmTimer *anim_timer;
float offset_x; /* offset to apply to node chain */
-} NodeInsertOfsData;
+};
static void clear_picking_highlight(ListBase *links)
{
@@ -189,8 +195,8 @@ static void clear_picking_highlight(ListBase *links)
static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock)
{
- LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data");
+ bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
if (sock->in_out == SOCK_OUT) {
oplink->fromnode = node;
@@ -224,6 +230,12 @@ static void pick_link(const bContext *C,
BLI_addtail(&nldrag->links, linkdata);
nodeRemLink(snode->edittree, link_to_pick);
+
+ BLI_assert(nldrag->last_node_hovered_while_dragging_a_link != nullptr);
+
+ sort_multi_input_socket_links(
+ snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, nullptr);
+
/* Send changed event to original link->tonode. */
if (node) {
snode_update(snode, node);
@@ -245,22 +257,12 @@ static void pick_input_link_by_link_intersect(const bContext *C,
bNodeSocket *socket;
node_find_indicated_socket(snode, &node, &socket, drag_start, SOCK_IN);
- const float trigger_drag_distance = 25.0f;
- const float cursor_link_touch_distance = 25.0f;
-
- const float socket_height = node_socket_calculate_height(socket);
-
- float cursor_to_socket_relative[2];
- float socket_position[2] = {socket->locx, socket->locy};
- sub_v2_v2v2(cursor_to_socket_relative, cursor, socket_position);
- float distance_from_socket_v2[2] = {
- max_ff(0, fabs(cursor_to_socket_relative[0]) - NODE_SOCKSIZE * 0.5),
- max_ff(0, fabs(cursor_to_socket_relative[1]) - socket_height)};
- const float distance_from_socket = len_v2(distance_from_socket_v2);
+ /* Distance to test overlapping of cursor on link. */
+ const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC;
const int resolution = NODE_LINK_RESOL;
- bNodeLink *link_to_pick = NULL;
+ bNodeLink *link_to_pick = nullptr;
clear_picking_highlight(&snode->edittree->links);
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (link->tosock == socket) {
@@ -301,7 +303,7 @@ static void pick_input_link_by_link_intersect(const bContext *C,
link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
ED_area_tag_redraw(CTX_wm_area(C));
- if (distance_from_socket > trigger_drag_distance) {
+ if (!node_find_indicated_socket(snode, &node, &socket, cursor, SOCK_IN)) {
pick_link(C, op, nldrag, snode, node, link_to_pick);
}
}
@@ -309,8 +311,8 @@ static void pick_input_link_by_link_intersect(const bContext *C,
static int sort_nodes_locx(const void *a, const void *b)
{
- const bNodeListItem *nli1 = a;
- const bNodeListItem *nli2 = b;
+ const bNodeListItem *nli1 = (const bNodeListItem *)a;
+ const bNodeListItem *nli2 = (const bNodeListItem *)b;
const bNode *node1 = nli1->node;
const bNode *node2 = nli2->node;
@@ -323,14 +325,14 @@ static int sort_nodes_locx(const void *a, const void *b)
static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
{
if (nodeSocketIsHidden(sock)) {
- return 0;
+ return false;
}
if (!allow_used && (sock->flag & SOCK_IN_USE)) {
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static bNodeSocket *best_socket_output(bNodeTree *ntree,
@@ -378,10 +380,10 @@ static bNodeSocket *best_socket_output(bNodeTree *ntree,
/* Always allow linking to an reroute node. The socket type of the reroute sockets might change
* after the link has been created. */
if (node->type == NODE_REROUTE) {
- return node->outputs.first;
+ return (bNodeSocket *)node->outputs.first;
}
- return NULL;
+ return nullptr;
}
/* this is a bit complicated, but designed to prioritize finding
@@ -413,7 +415,7 @@ static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, in
}
}
- return NULL;
+ return nullptr;
}
static bool snode_autoconnect_input(SpaceNode *snode,
@@ -434,10 +436,10 @@ static bool snode_autoconnect_input(SpaceNode *snode,
return true;
}
-typedef struct LinkAndPosition {
+struct LinkAndPosition {
struct bNodeLink *link;
float multi_socket_position[2];
-} LinkAndPosition;
+};
static int compare_link_by_y_position(const void *a, const void *b)
{
@@ -461,14 +463,14 @@ void sort_multi_input_socket_links(SpaceNode *snode,
}
/* The total is calculated in #node_update_nodetree, which runs before this draw step. */
int total_inputs = socket->total_inputs + 1;
- struct LinkAndPosition **input_links = MEM_malloc_arrayN(
+ struct LinkAndPosition **input_links = (LinkAndPosition **)MEM_malloc_arrayN(
total_inputs, sizeof(LinkAndPosition *), __func__);
int index = 0;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (link->tosock == socket) {
- struct LinkAndPosition *link_and_position = MEM_callocN(sizeof(struct LinkAndPosition),
- __func__);
+ struct LinkAndPosition *link_and_position = (LinkAndPosition *)MEM_callocN(
+ sizeof(struct LinkAndPosition), __func__);
link_and_position->link = link;
node_link_calculate_multi_input_position(link->tosock->locx,
link->tosock->locy,
@@ -481,7 +483,8 @@ void sort_multi_input_socket_links(SpaceNode *snode,
}
if (drag_link) {
- LinkAndPosition *link_and_position = MEM_callocN(sizeof(LinkAndPosition), __func__);
+ LinkAndPosition *link_and_position = (LinkAndPosition *)MEM_callocN(sizeof(LinkAndPosition),
+ __func__);
link_and_position->link = drag_link;
copy_v2_v2(link_and_position->multi_socket_position, cursor);
input_links[index] = link_and_position;
@@ -509,11 +512,12 @@ static void snode_autoconnect(Main *bmain,
const bool replace)
{
bNodeTree *ntree = snode->edittree;
- ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
+ ListBase *nodelist = (ListBase *)MEM_callocN(sizeof(ListBase), "items_list");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & NODE_SELECT) {
- bNodeListItem *nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
+ bNodeListItem *nli = (bNodeListItem *)MEM_mallocN(sizeof(bNodeListItem),
+ "temporary node list item");
nli->node = node;
BLI_addtail(nodelist, nli);
}
@@ -526,7 +530,7 @@ static void snode_autoconnect(Main *bmain,
LISTBASE_FOREACH (bNodeListItem *, nli, nodelist) {
bool has_selected_inputs = false;
- if (nli->next == NULL) {
+ if (nli->next == nullptr) {
break;
}
@@ -540,7 +544,7 @@ static void snode_autoconnect(Main *bmain,
/* if there are selected sockets, connect those */
LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) {
if (sock_to->flag & SELECT) {
- has_selected_inputs = 1;
+ has_selected_inputs = true;
if (!socket_is_available(ntree, sock_to, replace)) {
continue;
@@ -592,14 +596,18 @@ static void snode_autoconnect(Main *bmain,
MEM_freeN(nodelist);
}
-/* *************************** link viewer op ******************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Link Viewer Operator
+ * \{ */
static int node_link_viewer(const bContext *C, bNode *tonode)
{
SpaceNode *snode = CTX_wm_space_node(C);
/* context check */
- if (tonode == NULL || BLI_listbase_is_empty(&tonode->outputs)) {
+ if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) {
return OPERATOR_CANCELLED;
}
if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
@@ -607,7 +615,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
}
/* get viewer */
- bNode *viewer_node = NULL;
+ bNode *viewer_node = nullptr;
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
if (node->flag & NODE_DO_OUTPUT) {
@@ -617,7 +625,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
}
}
/* no viewer, we make one active */
- if (viewer_node == NULL) {
+ if (viewer_node == nullptr) {
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
node->flag |= NODE_DO_OUTPUT;
@@ -627,14 +635,14 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
}
}
- bNodeSocket *sock = NULL;
- bNodeLink *link = NULL;
+ bNodeSocket *sock = nullptr;
+ bNodeLink *link = nullptr;
/* try to find an already connected socket to cycle to the next */
if (viewer_node) {
- link = NULL;
+ link = nullptr;
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (link->tonode == viewer_node && link->fromnode == tonode) {
if (link->tosock == viewer_node->inputs.first) {
break;
@@ -667,7 +675,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
/* find a socket starting from the first socket */
if (!sock) {
- for (sock = tonode->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
if (!nodeSocketIsHidden(sock)) {
break;
}
@@ -678,24 +686,25 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
/* add a new viewer if none exists yet */
if (!viewer_node) {
/* XXX location is a quick hack, just place it next to the linked socket */
- viewer_node = node_add_node(C, NULL, CMP_NODE_VIEWER, sock->locx + 100, sock->locy);
+ viewer_node = node_add_node(C, nullptr, CMP_NODE_VIEWER, sock->locx + 100, sock->locy);
if (!viewer_node) {
return OPERATOR_CANCELLED;
}
- link = NULL;
+ link = nullptr;
}
else {
/* get link to viewer */
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) {
break;
}
}
}
- if (link == NULL) {
- nodeAddLink(snode->edittree, tonode, sock, viewer_node, viewer_node->inputs.first);
+ if (link == nullptr) {
+ nodeAddLink(
+ snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first);
}
else {
link->fromnode = tonode;
@@ -745,7 +754,11 @@ void NODE_OT_link_viewer(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* *************************** add link op ******************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Link Operator
+ * \{ */
static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
{
@@ -755,14 +768,11 @@ static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
ED_workspace_status_text(C, header);
}
-static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
+static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
{
int count = 0;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
- if (link->fromsock == sock) {
- count++;
- }
- if (link->tosock == sock) {
+ if (ELEM(socket, link->fromsock, link->tosock)) {
count++;
}
}
@@ -786,7 +796,7 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
if (tlink && tlink->fromsock == from) {
if (from_count > from_link_limit) {
nodeRemLink(ntree, tlink);
- tlink = NULL;
+ tlink = nullptr;
from_count--;
}
}
@@ -794,13 +804,13 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
if (tlink && tlink->tosock == to) {
if (to_count > to_link_limit) {
nodeRemLink(ntree, tlink);
- tlink = NULL;
+ tlink = nullptr;
to_count--;
}
else if (tlink->fromsock == from) {
/* Also remove link if it comes from the same output. */
nodeRemLink(ntree, tlink);
- tlink = NULL;
+ tlink = nullptr;
to_count--;
from_count--;
}
@@ -813,13 +823,13 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNodeLinkDrag *nldrag = op->customdata;
+ bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
bool do_tag_update = false;
/* avoid updates while applying links */
ntree->is_updating = true;
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
/* See note below, but basically TEST flag means that the link
* was connected to output (or to a node which affects the
@@ -859,8 +869,6 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
}
ntree->is_updating = false;
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(bmain, ntree);
snode_notify(C, snode);
if (do_tag_update) {
@@ -876,14 +884,14 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNodeLinkDrag *nldrag = op->customdata;
+ bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
if (nldrag->in_out == SOCK_OUT) {
bNode *tnode;
- bNodeSocket *tsock = NULL;
+ bNodeSocket *tsock = nullptr;
if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
/* skip if socket is on the same node as the fromsock */
if (tnode && link->fromnode == tnode) {
@@ -891,7 +899,7 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
}
/* Skip if tsock is already linked with this output. */
- bNodeLink *existing_link_connected_to_fromsock = NULL;
+ bNodeLink *existing_link_connected_to_fromsock = nullptr;
LISTBASE_FOREACH (bNodeLink *, existing_link, &snode->edittree->links) {
if (existing_link->fromsock == link->fromsock && existing_link->tosock == tsock) {
existing_link_connected_to_fromsock = existing_link;
@@ -908,27 +916,29 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
existing_link_connected_to_fromsock->multi_input_socket_index;
continue;
}
- sort_multi_input_socket_links(snode, tnode, link, cursor);
+ if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) {
+ sort_multi_input_socket_links(snode, tnode, link, cursor);
+ }
}
}
else {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
if (nldrag->last_node_hovered_while_dragging_a_link) {
sort_multi_input_socket_links(
- snode, nldrag->last_node_hovered_while_dragging_a_link, NULL, cursor);
+ snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, cursor);
}
- link->tonode = NULL;
- link->tosock = NULL;
+ link->tonode = nullptr;
+ link->tosock = nullptr;
}
}
}
else {
bNode *tnode;
- bNodeSocket *tsock = NULL;
+ bNodeSocket *tsock = nullptr;
if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
/* skip if this is already the target socket */
if (link->fromsock == tsock) {
@@ -946,10 +956,10 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
}
else {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
- link->fromnode = NULL;
- link->fromsock = NULL;
+ link->fromnode = nullptr;
+ link->fromsock = nullptr;
}
}
}
@@ -959,7 +969,7 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
/* in_out = starting socket */
static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
- bNodeLinkDrag *nldrag = op->customdata;
+ bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
ARegion *region = CTX_wm_region(C);
float cursor[2];
@@ -984,7 +994,7 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (event->val == KM_RELEASE) {
node_link_exit(C, op, true);
- ED_workspace_status_text(C, NULL);
+ ED_workspace_status_text(C, nullptr);
ED_region_tag_redraw(region);
SpaceNode *snode = CTX_wm_space_node(C);
clear_picking_highlight(&snode->edittree->links);
@@ -1000,13 +1010,13 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* return 1 when socket clicked */
static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor[2], bool detach)
{
- bNodeLinkDrag *nldrag = NULL;
+ bNodeLinkDrag *nldrag = nullptr;
/* output indicated? */
bNode *node;
bNodeSocket *sock;
if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
- nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
+ nldrag = (bNodeLinkDrag *)MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
const int num_links = nodeCountSocketLinks(snode->edittree, sock);
int link_limit = nodeSocketLinkLimit(sock);
@@ -1016,11 +1026,11 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
/* detach current links and store them in the operator data */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) {
if (link->fromsock == sock) {
- LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data");
+ bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
*oplink = *link;
- oplink->next = oplink->prev = NULL;
+ oplink->next = oplink->prev = nullptr;
oplink->flag |= NODE_LINK_VALID;
/* The link could be disconnected and in that case we
@@ -1050,7 +1060,8 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
}
/* or an input? */
else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
- nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
+ nldrag = (bNodeLinkDrag *)MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
+ nldrag->last_node_hovered_while_dragging_a_link = node;
const int num_links = nodeCountSocketLinks(snode->edittree, sock);
if (num_links > 0) {
@@ -1067,12 +1078,12 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
}
}
- if (link_to_pick != NULL && !nldrag->from_multi_input_socket) {
- LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ if (link_to_pick != nullptr && !nldrag->from_multi_input_socket) {
+ LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data");
+ bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
*oplink = *link_to_pick;
- oplink->next = oplink->prev = NULL;
+ oplink->next = oplink->prev = nullptr;
oplink->flag |= NODE_LINK_VALID;
oplink->flag &= ~NODE_LINK_TEST;
if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) {
@@ -1133,7 +1144,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void node_link_cancel(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNodeLinkDrag *nldrag = op->customdata;
+ bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
BLI_remlink(&snode->runtime->linkdrag, nldrag);
@@ -1173,7 +1184,7 @@ void NODE_OT_link(wmOperatorType *ot)
RNA_def_float_array(ot->srna,
"drag_start",
2,
- 0,
+ nullptr,
-UI_PRECISION_FLOAT_MAX,
UI_PRECISION_FLOAT_MAX,
"Drag Start",
@@ -1184,7 +1195,11 @@ void NODE_OT_link(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
-/* ********************** Make Link operator ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Make Link Operator
+ * \{ */
/* makes a link between selected output and input sockets */
static int node_make_link_exec(bContext *C, wmOperator *op)
@@ -1195,11 +1210,11 @@ static int node_make_link_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- snode_autoconnect(bmain, snode, 1, replace);
+ snode_autoconnect(bmain, snode, true, replace);
/* deselect sockets after linking */
- node_deselect_all_input_sockets(snode, 0);
- node_deselect_all_output_sockets(snode, 0);
+ node_deselect_all_input_sockets(snode, false);
+ node_deselect_all_output_sockets(snode, false);
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
@@ -1224,27 +1239,37 @@ void NODE_OT_link_make(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(
- ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
+ ot->srna, "replace", false, "Replace", "Replace socket connections with the new links");
}
-/* ********************** Node Link Intersect ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Link Intersect
+ * \{ */
+
static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot)
{
float coord_array[NODE_LINK_RESOL + 1][2];
- if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
+ if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) {
for (int i = 0; i < tot - 1; i++) {
for (int b = 0; b < NODE_LINK_RESOL; b++) {
if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) {
- return 1;
+ return true;
}
}
}
}
- return 0;
+ return false;
}
-/* ********************** Cut Link operator ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cut Link Operator
+ * \{ */
+
static int cut_links_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1291,12 +1316,10 @@ static int cut_links_exec(bContext *C, wmOperator *op)
snode_update(snode, link->tonode);
bNode *to_node = link->tonode;
nodeRemLink(snode->edittree, link);
- sort_multi_input_socket_links(snode, to_node, NULL, NULL);
+ sort_multi_input_socket_links(snode, to_node, nullptr, nullptr);
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
if (found) {
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
@@ -1332,13 +1355,17 @@ void NODE_OT_links_cut(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
/* internal */
RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
}
-/* ********************** Mute links operator ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mute Links Operator
+ * \{ */
static int mute_links_exec(bContext *C, wmOperator *op)
{
@@ -1403,8 +1430,6 @@ static int mute_links_exec(bContext *C, wmOperator *op)
link->flag &= ~NODE_LINK_TEST;
}
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
if (do_tag_update) {
@@ -1436,13 +1461,17 @@ void NODE_OT_links_mute(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
/* internal */
RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
}
-/* ********************** Detach links operator ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Detach Links Operator
+ * \{ */
static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -1479,7 +1508,11 @@ void NODE_OT_links_detach(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Set Parent ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Set Parent Operator
+ * \{ */
static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -1501,7 +1534,7 @@ static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1521,7 +1554,11 @@ void NODE_OT_parent_set(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Join Nodes ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Join Nodes Operator
+ * \{ */
/* tags for depth-first search */
#define NODE_JOIN_DONE 1
@@ -1573,7 +1610,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- bNode *frame = node_add_node(C, NULL, NODE_FRAME, 0.0f, 0.0f);
+ bNode *frame = node_add_node(C, nullptr, NODE_FRAME, 0.0f, 0.0f);
/* reset tags */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -1594,7 +1631,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1614,7 +1651,11 @@ void NODE_OT_join(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Attach ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attach Operator
+ * \{ */
static bNode *node_find_frame_to_attach(ARegion *region,
const bNodeTree *ntree,
@@ -1634,7 +1675,7 @@ static bNode *node_find_frame_to_attach(ARegion *region,
}
}
- return NULL;
+ return nullptr;
}
static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
@@ -1647,7 +1688,7 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
if (frame) {
LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) {
if (node->flag & NODE_SELECT) {
- if (node->parent == NULL) {
+ if (node->parent == nullptr) {
/* disallow moving a parent into its child */
if (nodeAttachNodeCheck(frame, node) == false) {
/* attach all unparented nodes */
@@ -1676,7 +1717,7 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1697,7 +1738,11 @@ void NODE_OT_attach(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Detach ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Detach Operator
+ * \{ */
/* tags for depth-first search */
#define NODE_DETACH_DONE 1
@@ -1748,7 +1793,7 @@ static int node_detach_exec(bContext *C, wmOperator *UNUSED(op))
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1768,7 +1813,11 @@ void NODE_OT_detach(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ********************* automatic node insert on dragging ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Automatic Node Insert on Dragging
+ * \{ */
/* prevent duplicate testing code below */
static bool ed_node_link_conditions(ScrArea *area,
@@ -1776,13 +1825,13 @@ static bool ed_node_link_conditions(ScrArea *area,
SpaceNode **r_snode,
bNode **r_select)
{
- SpaceNode *snode = area ? area->spacedata.first : NULL;
+ SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr;
*r_snode = snode;
- *r_select = NULL;
+ *r_select = nullptr;
/* no unlucky accidents */
- if (area == NULL || area->spacetype != SPACE_NODE) {
+ if (area == nullptr || area->spacetype != SPACE_NODE) {
return false;
}
@@ -1792,8 +1841,8 @@ static bool ed_node_link_conditions(ScrArea *area,
}
bNode *node;
- bNode *select = NULL;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ bNode *select = nullptr;
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (node->flag & SELECT) {
if (select) {
break;
@@ -1802,7 +1851,7 @@ static bool ed_node_link_conditions(ScrArea *area,
}
}
/* only one selected */
- if (node || select == NULL) {
+ if (node || select == nullptr) {
return false;
}
@@ -1845,7 +1894,7 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
}
/* find link to select/highlight */
- bNodeLink *selink = NULL;
+ bNodeLink *selink = nullptr;
float dist_best = FLT_MAX;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
float coord_array[NODE_LINK_RESOL + 1][2];
@@ -1854,7 +1903,7 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
continue;
}
- if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
+ if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) {
float dist = FLT_MAX;
/* loop over link coords to find shortest dist to
@@ -1886,35 +1935,76 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
}
}
-/* assumes sockets in list */
-static bNodeSocket *socket_best_match(ListBase *sockets)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Insert Offset Operator
+ * \{ */
+
+static int get_main_socket_priority(const bNodeSocket *socket)
{
- /* find type range */
- int maxtype = 0;
+ switch ((eNodeSocketDatatype)socket->type) {
+ case __SOCK_MESH:
+ case SOCK_CUSTOM:
+ return -1;
+ case SOCK_BOOLEAN:
+ return 0;
+ case SOCK_INT:
+ return 1;
+ case SOCK_FLOAT:
+ return 2;
+ case SOCK_VECTOR:
+ return 3;
+ case SOCK_RGBA:
+ return 4;
+ case SOCK_STRING:
+ case SOCK_SHADER:
+ case SOCK_OBJECT:
+ case SOCK_IMAGE:
+ case SOCK_GEOMETRY:
+ case SOCK_COLLECTION:
+ case SOCK_TEXTURE:
+ case SOCK_MATERIAL:
+ return 5;
+ }
+ return -1;
+}
+
+/** Get the "main" socket of a socket list using a heuristic based on socket types. */
+static bNodeSocket *get_main_socket(ListBase *sockets)
+{
+ /* find priority range */
+ int maxpriority = -1;
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- maxtype = max_ii(sock->type, maxtype);
+ if (sock->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ maxpriority = max_ii(get_main_socket_priority(sock), maxpriority);
}
- /* try all types, starting from 'highest' (i.e. colors, vectors, values) */
- for (int type = maxtype; type >= 0; type--) {
+ /* try all priorities, starting from 'highest' */
+ for (int priority = maxpriority; priority >= 0; priority--) {
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- if (!nodeSocketIsHidden(sock) && type == sock->type) {
+ if (!nodeSocketIsHidden(sock) && priority == get_main_socket_priority(sock)) {
return sock;
}
}
}
- /* no visible sockets, unhide first of highest type */
- for (int type = maxtype; type >= 0; type--) {
+ /* no visible sockets, unhide first of highest priority */
+ for (int priority = maxpriority; priority >= 0; priority--) {
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- if (type == sock->type) {
+ if (sock->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ if (priority == get_main_socket_priority(sock)) {
sock->flag &= ~SOCK_HIDDEN;
return sock;
}
}
}
- return NULL;
+ return nullptr;
}
static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
@@ -1952,7 +2042,7 @@ static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, con
#define NODE_INSOFS_ANIM_DURATION 0.25f
/**
- * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, similar
+ * Callback that applies #NodeInsertOfsData.offset_x to a node or its parent, similar
* to node_link_insert_offset_output_chain_cb below, but with slightly different logic
*/
static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode,
@@ -1960,7 +2050,7 @@ static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode,
void *userdata,
const bool reversed)
{
- NodeInsertOfsData *data = userdata;
+ NodeInsertOfsData *data = (NodeInsertOfsData *)userdata;
bNode *ofs_node = reversed ? fromnode : tonode;
if (ofs_node->parent && ofs_node->parent != data->insert_parent) {
@@ -1997,7 +2087,7 @@ static bool node_link_insert_offset_chain_cb(bNode *fromnode,
void *userdata,
const bool reversed)
{
- NodeInsertOfsData *data = userdata;
+ NodeInsertOfsData *data = (NodeInsertOfsData *)userdata;
bNode *ofs_node = reversed ? fromnode : tonode;
if (data->insert_parent) {
@@ -2010,7 +2100,7 @@ static bool node_link_insert_offset_chain_cb(bNode *fromnode,
}
if (nodeIsChildOf(data->insert_parent, ofs_node) == false) {
- data->insert_parent = NULL;
+ data->insert_parent = nullptr;
}
}
else if (ofs_node->parent) {
@@ -2061,7 +2151,7 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd,
rctf totr_frame;
/* check nodes front to back */
- for (frame = ntree->nodes.last; frame; frame = frame->prev) {
+ for (frame = (bNode *)ntree->nodes.last; frame; frame = frame->prev) {
/* skip selected, those are the nodes we want to attach */
if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
continue;
@@ -2127,7 +2217,7 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd,
iofsd->offset_x = margin;
/* flag all parents of insert as offset to prevent them from being offset */
- nodeParentsIter(insert, node_parents_offset_flag_enable_cb, NULL);
+ nodeParentsIter(insert, node_parents_offset_flag_enable_cb, nullptr);
/* iterate over entire chain and apply offsets */
nodeChainIter(ntree,
right_alignment ? next : prev,
@@ -2148,7 +2238,8 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w
NodeInsertOfsData *iofsd = snode->runtime->iofsd;
bool redraw = false;
- if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata) {
+ if (!snode || event->type != TIMER || iofsd == nullptr ||
+ iofsd->anim_timer != event->customdata) {
return OPERATOR_PASS_THROUGH;
}
@@ -2178,13 +2269,13 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w
/* end timer + free insert offset data */
if (duration > NODE_INSOFS_ANIM_DURATION) {
- WM_event_remove_timer(CTX_wm_manager(C), NULL, iofsd->anim_timer);
+ WM_event_remove_timer(CTX_wm_manager(C), nullptr, iofsd->anim_timer);
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
node->anim_init_locx = node->anim_ofsx = 0.0f;
}
- snode->runtime->iofsd = NULL;
+ snode->runtime->iofsd = nullptr;
MEM_freeN(iofsd);
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
@@ -2234,6 +2325,12 @@ void NODE_OT_insert_offset(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Note Link Insert
+ * \{ */
+
/* assumes link with NODE_LINKFLAG_HILITE set */
void ED_node_link_insert(Main *bmain, ScrArea *area)
{
@@ -2245,15 +2342,15 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
/* get the link */
bNodeLink *link;
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (link->flag & NODE_LINKFLAG_HILITE) {
break;
}
}
if (link) {
- bNodeSocket *best_input = socket_best_match(&select->inputs);
- bNodeSocket *best_output = socket_best_match(&select->outputs);
+ bNodeSocket *best_input = get_main_socket(&select->inputs);
+ bNodeSocket *best_output = get_main_socket(&select->outputs);
if (best_input && best_output) {
bNode *node = link->tonode;
@@ -2264,11 +2361,17 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
node_remove_extra_links(snode, link);
link->flag &= ~NODE_LINKFLAG_HILITE;
- nodeAddLink(snode->edittree, select, best_output, node, sockto);
+ bNodeLink *new_link = nodeAddLink(snode->edittree, select, best_output, node, sockto);
+
+ /* Copy the socket index for the new link, and reset it for the old link. This way the
+ * relative order of links is preserved, and the links get drawn in the right place. */
+ new_link->multi_input_socket_index = link->multi_input_socket_index;
+ link->multi_input_socket_index = 0;
/* set up insert offset data, it needs stuff from here */
if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
- NodeInsertOfsData *iofsd = MEM_callocN(sizeof(NodeInsertOfsData), __func__);
+ NodeInsertOfsData *iofsd = (NodeInsertOfsData *)MEM_callocN(sizeof(NodeInsertOfsData),
+ __func__);
iofsd->insert = select;
iofsd->prev = link->fromnode;
@@ -2281,8 +2384,8 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
snode_update(snode, select);
ED_node_tag_update_id((ID *)snode->edittree);
ED_node_tag_update_id(snode->id);
-
- sort_multi_input_socket_links(snode, node, NULL, NULL);
}
}
}
+
+/** \} */
diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.cc
index 1da79671c8e..41820cd813c 100644
--- a/source/blender/editors/space_node/node_select.c
+++ b/source/blender/editors/space_node/node_select.cc
@@ -21,7 +21,8 @@
* \ingroup spnode
*/
-#include <stdlib.h>
+#include <array>
+#include <cstdlib>
#include "DNA_node_types.h"
#include "DNA_windowmanager_types.h"
@@ -81,7 +82,7 @@ static bool has_workbench_in_texture_color(const wmWindowManager *wm,
const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_VIEW3D) {
- const View3D *v3d = area->spacedata.first;
+ const View3D *v3d = (const View3D *)area->spacedata.first;
if (ED_view3d_has_workbench_in_texture_color(scene, ob, v3d)) {
return true;
@@ -100,28 +101,28 @@ static bNode *node_under_mouse_select(bNodeTree *ntree, int mx, int my)
{
bNode *node;
- for (node = ntree->nodes.last; node; node = node->prev) {
+ for (node = (bNode *)ntree->nodes.last; node; node = node->prev) {
if (node->typeinfo->select_area_func) {
if (node->typeinfo->select_area_func(node, mx, my)) {
return node;
}
}
}
- return NULL;
+ return nullptr;
}
static bNode *node_under_mouse_tweak(bNodeTree *ntree, int mx, int my)
{
bNode *node;
- for (node = ntree->nodes.last; node; node = node->prev) {
+ for (node = (bNode *)ntree->nodes.last; node; node = node->prev) {
if (node->typeinfo->tweak_area_func) {
if (node->typeinfo->tweak_area_func(node, mx, my)) {
return node;
}
}
}
- return NULL;
+ return nullptr;
}
static bool is_position_over_node_or_socket(SpaceNode *snode, float mouse[2])
@@ -168,18 +169,18 @@ void node_socket_deselect(bNode *node, bNodeSocket *sock, const bool deselect_no
sock->flag &= ~SELECT;
if (node && deselect_node) {
- bool sel = 0;
+ bool sel = false;
/* if no selected sockets remain, also deselect the node */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (sock->flag & SELECT) {
- sel = 1;
+ sel = true;
break;
}
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
if (sock->flag & SELECT) {
- sel = 1;
+ sel = true;
break;
}
}
@@ -205,7 +206,7 @@ void node_deselect_all(SpaceNode *snode)
{
bNode *node;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
nodeSetSelected(node, false);
}
}
@@ -220,16 +221,16 @@ void node_deselect_all_input_sockets(SpaceNode *snode, const bool deselect_nodes
* We can do that more efficiently here.
*/
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
int sel = 0;
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
sock->flag &= ~SELECT;
}
/* if no selected sockets remain, also deselect the node */
if (deselect_nodes) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
if (sock->flag & SELECT) {
sel = 1;
break;
@@ -253,18 +254,18 @@ void node_deselect_all_output_sockets(SpaceNode *snode, const bool deselect_node
* We can do that more efficiently here.
*/
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
bool sel = false;
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
sock->flag &= ~SELECT;
}
/* if no selected sockets remain, also deselect the node */
if (deselect_nodes) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (sock->flag & SELECT) {
- sel = 1;
+ sel = true;
break;
}
}
@@ -289,7 +290,7 @@ static bool node_select_grouped_type(SpaceNode *snode, bNode *node_act)
bNode *node;
bool changed = false;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if ((node->flag & SELECT) == 0) {
if (node->type == node_act->type) {
nodeSetSelected(node, true);
@@ -306,7 +307,7 @@ static bool node_select_grouped_color(SpaceNode *snode, bNode *node_act)
bNode *node;
bool changed = false;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if ((node->flag & SELECT) == 0) {
if (compare_v3v3(node->color, node_act->color, 0.005f)) {
nodeSetSelected(node, true);
@@ -327,7 +328,7 @@ static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bo
const char *sep, *suf_act, *suf_curr;
pref_len_act = BLI_str_partition_ex_utf8(
- node_act->name, NULL, delims, &sep, &suf_act, from_right);
+ node_act->name, nullptr, delims, &sep, &suf_act, from_right);
/* Note: in case we are searching for suffix, and found none, use whole name as suffix. */
if (from_right && !(sep && suf_act)) {
@@ -335,12 +336,12 @@ static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bo
suf_act = node_act->name;
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (node->flag & SELECT) {
continue;
}
pref_len_curr = BLI_str_partition_ex_utf8(
- node->name, NULL, delims, &sep, &suf_curr, from_right);
+ node->name, nullptr, delims, &sep, &suf_curr, from_right);
/* Same as with active node name! */
if (from_right && !(sep && suf_curr)) {
@@ -371,7 +372,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNode *node_act = nodeGetActive(snode->edittree);
- if (node_act == NULL) {
+ if (node_act == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -381,7 +382,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op)
const int type = RNA_enum_get(op->ptr, "type");
if (!extend) {
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
nodeSetSelected(node, false);
}
}
@@ -406,7 +407,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op)
if (changed) {
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -420,7 +421,7 @@ void NODE_OT_select_grouped(wmOperatorType *ot)
{NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""},
{NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""},
{NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
@@ -461,7 +462,7 @@ void node_select_single(bContext *C, bNode *node)
bool active_texture_changed = false;
bNode *tnode;
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
if (tnode != node) {
nodeSetSelected(tnode, false);
}
@@ -476,7 +477,7 @@ void node_select_single(bContext *C, bNode *node)
DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE);
}
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
}
static int node_mouse_select(bContext *C,
@@ -491,7 +492,7 @@ static int node_mouse_select(bContext *C,
const Scene *scene = CTX_data_scene(C);
const wmWindowManager *wm = CTX_wm_manager(C);
bNode *node, *tnode;
- bNodeSocket *sock = NULL;
+ bNodeSocket *sock = nullptr;
bNodeSocket *tsock;
float cursor[2];
int ret_value = OPERATOR_CANCELLED;
@@ -530,7 +531,7 @@ static int node_mouse_select(bContext *C,
/* Only allow one selected output per node, for sensible linking.
* Allow selecting outputs from different nodes though, if extend is true. */
if (node) {
- for (tsock = node->outputs.first; tsock; tsock = tsock->next) {
+ for (tsock = (bNodeSocket *)node->outputs.first; tsock; tsock = tsock->next) {
if (tsock == sock) {
continue;
}
@@ -538,11 +539,11 @@ static int node_mouse_select(bContext *C,
}
}
if (!extend) {
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
if (tnode == node) {
continue;
}
- for (tsock = tnode->outputs.first; tsock; tsock = tsock->next) {
+ for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) {
node_socket_deselect(tnode, tsock, true);
}
}
@@ -558,7 +559,7 @@ static int node_mouse_select(bContext *C,
node = node_under_mouse_select(snode->edittree, (int)cursor[0], (int)cursor[1]);
if (extend) {
- if (node != NULL) {
+ if (node != nullptr) {
/* If node is selected but not active, we want to make it active,
* but not toggle (deselect) it. */
if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) {
@@ -567,14 +568,22 @@ static int node_mouse_select(bContext *C,
ret_value = OPERATOR_FINISHED;
}
}
- else if (deselect_all && node == NULL) {
- /* Deselect in empty space. */
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
- nodeSetSelected(tnode, false);
+ else if (deselect_all && node == nullptr) {
+ /* Rather than deselecting others, users may want to drag to box-select (drag from empty
+ * space) or tweak-translate an already selected item. If these cases may apply, delay
+ * deselection. */
+ if (wait_to_deselect_others) {
+ ret_value = OPERATOR_RUNNING_MODAL;
+ }
+ else {
+ /* Deselect in empty space. */
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ nodeSetSelected(tnode, false);
+ }
+ ret_value = OPERATOR_FINISHED;
}
- ret_value = OPERATOR_FINISHED;
}
- else if (node != NULL) {
+ else if (node != nullptr) {
/* When clicking on an already selected node, we want to wait to deselect
* others and allow the user to start moving the node without that. */
if (wait_to_deselect_others && (node->flag & SELECT)) {
@@ -583,7 +592,7 @@ static int node_mouse_select(bContext *C,
else {
nodeSetSelected(node, true);
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
if (tnode != node) {
nodeSetSelected(tnode, false);
}
@@ -597,7 +606,7 @@ static int node_mouse_select(bContext *C,
/* update node order */
if (ret_value != OPERATOR_CANCELLED) {
bool active_texture_changed = false;
- if (node != NULL && ret_value != OPERATOR_RUNNING_MODAL) {
+ if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) {
ED_node_set_active(bmain, snode->edittree, node, &active_texture_changed);
}
ED_node_set_active_viewer_key(snode);
@@ -606,7 +615,7 @@ static int node_mouse_select(bContext *C,
DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE);
}
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
}
return ret_value;
@@ -673,7 +682,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op)
WM_operator_properties_border_to_rctf(op, &rectf);
UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
- const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
+ const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT);
@@ -685,7 +694,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op)
is_inside = BLI_rctf_inside_rctf(&rectf, &node->totr);
}
else {
- is_inside = BLI_rctf_isect(&rectf, &node->totr, NULL);
+ is_inside = BLI_rctf_isect(&rectf, &node->totr, nullptr);
}
if (is_inside) {
@@ -695,7 +704,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op)
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -732,7 +741,7 @@ void NODE_OT_select_box(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna,
"tweak",
- 0,
+ false,
"Tweak",
"Only activate when mouse is not over a node (useful for tweak gesture)");
@@ -758,8 +767,9 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
float zoom = (float)(BLI_rcti_size_x(&region->winrct)) /
(float)(BLI_rctf_size_x(&region->v2d.cur));
- const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
- WM_gesture_is_modal_first(op->customdata));
+ const eSelectOp sel_op = ED_select_op_modal(
+ (eSelectOp)RNA_enum_get(op->ptr, "mode"),
+ WM_gesture_is_modal_first((const wmGesture *)op->customdata));
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT);
@@ -772,13 +782,13 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) {
nodeSetSelected(node, select);
}
}
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -845,7 +855,7 @@ static bool do_lasso_select_node(bContext *C,
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
/* do actual selection */
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (select && (node->flag & NODE_SELECT)) {
continue;
@@ -865,7 +875,7 @@ static bool do_lasso_select_node(bContext *C,
}
if (changed) {
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
}
return changed;
@@ -877,7 +887,7 @@ static int node_lasso_select_exec(bContext *C, wmOperator *op)
const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len);
if (mcoords) {
- const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
+ const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
do_lasso_select_node(C, mcoords, mcoords_len, sel_op);
@@ -908,7 +918,7 @@ void NODE_OT_select_lasso(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna,
"tweak",
- 0,
+ false,
"Tweak",
"Only activate when mouse is not over a node (useful for tweak gesture)");
@@ -932,7 +942,7 @@ static int node_select_all_exec(bContext *C, wmOperator *op)
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -965,11 +975,11 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
bNodeLink *link;
bNode *node;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
node->flag &= ~NODE_TEST;
}
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (nodeLinkIsHidden(link)) {
continue;
}
@@ -978,7 +988,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (node->flag & NODE_TEST) {
nodeSetSelected(node, true);
}
@@ -986,7 +996,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -1017,11 +1027,11 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
bNodeLink *link;
bNode *node;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
node->flag &= ~NODE_TEST;
}
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (nodeLinkIsHidden(link)) {
continue;
}
@@ -1030,7 +1040,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (node->flag & NODE_TEST) {
nodeSetSelected(node, true);
}
@@ -1038,7 +1048,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -1071,7 +1081,7 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op)
bNode *active = nodeGetActive(snode->edittree);
int totnodes;
const bool revert = RNA_boolean_get(op->ptr, "prev");
- const bool same_type = 1;
+ const bool same_type = true;
ntreeGetDependencyList(snode->edittree, &node_array, &totnodes);
@@ -1085,9 +1095,9 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op)
}
if (same_type) {
- bNode *node = NULL;
+ bNode *node = nullptr;
- while (node == NULL) {
+ while (node == nullptr) {
if (revert) {
a--;
}
@@ -1104,7 +1114,7 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op)
if (node->type == active->type) {
break;
}
- node = NULL;
+ node = nullptr;
}
if (node) {
active = node;
@@ -1160,7 +1170,7 @@ void NODE_OT_select_same_type_step(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_boolean(ot->srna, "prev", 0, "Previous", "");
+ RNA_def_boolean(ot->srna, "prev", false, "Previous", "");
}
/** \} */
@@ -1215,7 +1225,7 @@ static void node_find_update_fn(const struct bContext *C,
static void node_find_exec_fn(struct bContext *C, void *UNUSED(arg1), void *arg2)
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *active = arg2;
+ bNode *active = (bNode *)arg2;
if (active) {
ARegion *region = CTX_wm_region(C);
@@ -1252,7 +1262,8 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op)
0,
0,
"");
- UI_but_func_search_set(but, NULL, node_find_update_fn, op->type, NULL, node_find_exec_fn, NULL);
+ UI_but_func_search_set(
+ but, nullptr, node_find_update_fn, op->type, false, nullptr, node_find_exec_fn, nullptr);
UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
/* fake button, it holds space for search items */
@@ -1264,22 +1275,23 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op)
10 - UI_searchbox_size_y(),
UI_searchbox_size_x(),
UI_searchbox_size_y(),
- NULL,
+ nullptr,
0,
0,
0,
0,
- NULL);
+ nullptr);
/* Move it downwards, mouse over button. */
- UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, (const int[2]){0, -UI_UNIT_Y});
+ std::array<int, 2> bounds_offset = {0, -UI_UNIT_Y};
+ UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, bounds_offset.data());
return block;
}
static int node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- UI_popup_block_invoke(C, node_find_menu, op, NULL);
+ UI_popup_block_invoke(C, node_find_menu, op, nullptr);
return OPERATOR_CANCELLED;
}
@@ -1297,7 +1309,7 @@ void NODE_OT_find_node(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_boolean(ot->srna, "prev", 0, "Previous", "");
+ RNA_def_boolean(ot->srna, "prev", false, "Previous", "");
}
/** \} */
diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.cc
index 3873985d93a..fae180ca6c5 100644
--- a/source/blender/editors/space_node/node_templates.c
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -18,8 +18,8 @@
* \ingroup edinterface
*/
-#include <stdlib.h>
-#include <string.h>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -29,6 +29,7 @@
#include "BLI_array.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
+#include "BLI_vector.hh"
#include "BLT_translation.h"
@@ -51,7 +52,7 @@
/************************* Node Socket Manipulation **************************/
/* describes an instance of a node type and a specific socket to link */
-typedef struct NodeLinkItem {
+struct NodeLinkItem {
int socket_index; /* index for linking */
int socket_type; /* socket type for compatibility check */
const char *socket_name; /* ui label of the socket */
@@ -59,7 +60,7 @@ typedef struct NodeLinkItem {
/* extra settings */
bNodeTree *ngroup; /* group node tree */
-} NodeLinkItem;
+};
/* Compare an existing node to a link item to see if it can be reused.
* item must be for the same node type!
@@ -98,7 +99,7 @@ static void node_tag_recursive(bNode *node)
node->flag |= NODE_TEST;
- for (input = node->inputs.first; input; input = input->next) {
+ for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
if (input->link) {
node_tag_recursive(input->link->fromnode);
}
@@ -115,7 +116,7 @@ static void node_clear_recursive(bNode *node)
node->flag &= ~NODE_TEST;
- for (input = node->inputs.first; input; input = input->next) {
+ for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
if (input->link) {
node_clear_recursive(input->link->fromnode);
}
@@ -132,16 +133,16 @@ static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
}
/* tag linked nodes to be removed */
- for (node = ntree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)ntree->nodes.first; node; node = node->next) {
node->flag &= ~NODE_TEST;
}
node_tag_recursive(rem_node);
/* clear tags on nodes that are still used by other nodes */
- for (node = ntree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)ntree->nodes.first; node; node = node->next) {
if (!(node->flag & NODE_TEST)) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (sock->link && sock->link->fromnode != rem_node) {
node_clear_recursive(sock->link->fromnode);
}
@@ -150,7 +151,7 @@ static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
}
/* remove nodes */
- for (node = ntree->nodes.first; node; node = next) {
+ for (node = (bNode *)ntree->nodes.first; node; node = next) {
next = node->next;
if (node->flag & NODE_TEST) {
@@ -205,7 +206,7 @@ static void node_socket_add_replace(const bContext *C,
Main *bmain = CTX_data_main(C);
bNode *node_from;
bNodeSocket *sock_from_tmp;
- bNode *node_prev = NULL;
+ bNode *node_prev = nullptr;
/* unlink existing node */
if (sock_to->link) {
@@ -214,7 +215,7 @@ static void node_socket_add_replace(const bContext *C,
}
/* find existing node that we can use */
- for (node_from = ntree->nodes.first; node_from; node_from = node_from->next) {
+ for (node_from = (bNode *)ntree->nodes.first; node_from; node_from = node_from->next) {
if (node_from->type == type) {
break;
}
@@ -223,7 +224,7 @@ static void node_socket_add_replace(const bContext *C,
if (node_from) {
if (node_from->inputs.first || node_from->typeinfo->draw_buttons ||
node_from->typeinfo->draw_buttons_ex) {
- node_from = NULL;
+ node_from = nullptr;
}
}
@@ -233,7 +234,7 @@ static void node_socket_add_replace(const bContext *C,
}
else if (!node_from) {
node_from = nodeAddStaticNode(C, ntree, type);
- if (node_prev != NULL) {
+ if (node_prev != nullptr) {
/* If we're replacing existing node, use its location. */
node_from->locx = node_prev->locx;
node_from->locy = node_prev->locy;
@@ -241,7 +242,7 @@ static void node_socket_add_replace(const bContext *C,
node_from->offsety = node_prev->offsety;
}
else {
- sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index);
+ sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
nodePositionRelative(node_from, node_to, sock_from_tmp, sock_to);
}
@@ -251,7 +252,7 @@ static void node_socket_add_replace(const bContext *C,
nodeSetActive(ntree, node_from);
/* add link */
- sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index);
+ sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
nodeAddLink(ntree, node_from, sock_from_tmp, node_to, sock_to);
sock_to->flag &= ~SOCK_COLLAPSED;
@@ -259,8 +260,10 @@ static void node_socket_add_replace(const bContext *C,
if (node_prev && node_from != node_prev) {
bNodeSocket *sock_prev, *sock_from;
- for (sock_prev = node_prev->inputs.first; sock_prev; sock_prev = sock_prev->next) {
- for (sock_from = node_from->inputs.first; sock_from; sock_from = sock_from->next) {
+ for (sock_prev = (bNodeSocket *)node_prev->inputs.first; sock_prev;
+ sock_prev = sock_prev->next) {
+ for (sock_from = (bNodeSocket *)node_from->inputs.first; sock_from;
+ sock_from = sock_from->next) {
if (nodeCountSocketLinks(ntree, sock_from) >= nodeSocketLinkLimit(sock_from)) {
continue;
}
@@ -282,7 +285,7 @@ static void node_socket_add_replace(const bContext *C,
if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE &&
node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE &&
/* White noise texture node does not have NodeTexBase. */
- node_from->storage != NULL && node_prev->storage != NULL) {
+ node_from->storage != nullptr && node_prev->storage != nullptr) {
memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase));
}
@@ -303,7 +306,7 @@ static void node_socket_add_replace(const bContext *C,
#define UI_NODE_LINK_DISCONNECT -1
#define UI_NODE_LINK_REMOVE -2
-typedef struct NodeLinkArg {
+struct NodeLinkArg {
Main *bmain;
Scene *scene;
bNodeTree *ntree;
@@ -314,7 +317,7 @@ typedef struct NodeLinkArg {
NodeLinkItem item;
uiLayout *layout;
-} NodeLinkArg;
+};
static void ui_node_link_items(NodeLinkArg *arg,
int in_out,
@@ -322,15 +325,18 @@ static void ui_node_link_items(NodeLinkArg *arg,
int *r_totitems)
{
/* XXX this should become a callback for node types! */
- NodeLinkItem *items = NULL;
+ NodeLinkItem *items = nullptr;
int totitems = 0;
if (arg->node_type->type == NODE_GROUP) {
bNodeTree *ngroup;
int i;
- for (ngroup = arg->bmain->nodetrees.first; ngroup; ngroup = ngroup->id.next) {
- if ((ngroup->type != arg->ntree->type) || !nodeGroupPoll(arg->ntree, ngroup)) {
+ for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
+ ngroup = (bNodeTree *)ngroup->id.next) {
+ const char *disabled_hint;
+ if ((ngroup->type != arg->ntree->type) ||
+ !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) {
continue;
}
@@ -339,18 +345,22 @@ static void ui_node_link_items(NodeLinkArg *arg,
}
if (totitems > 0) {
- items = MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items");
+ items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items");
i = 0;
- for (ngroup = arg->bmain->nodetrees.first; ngroup; ngroup = ngroup->id.next) {
- if ((ngroup->type != arg->ntree->type) || !nodeGroupPoll(arg->ntree, ngroup)) {
+ for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
+ ngroup = (bNodeTree *)ngroup->id.next) {
+ const char *disabled_hint;
+ if ((ngroup->type != arg->ntree->type) ||
+ !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) {
continue;
}
ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs);
bNodeSocket *stemp;
int index;
- for (stemp = lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) {
+ for (stemp = (bNodeSocket *)lb->first, index = 0; stemp;
+ stemp = stemp->next, index++, i++) {
NodeLinkItem *item = &items[i];
item->socket_index = index;
@@ -376,7 +386,7 @@ static void ui_node_link_items(NodeLinkArg *arg,
}
if (totitems > 0) {
- items = MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items");
+ items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items");
i = 0;
for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) {
@@ -470,18 +480,18 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
bNodeTree *ntree = arg->ntree;
bNodeSocket *sock = arg->sock;
uiLayout *layout = arg->layout;
- uiLayout *column = NULL;
+ uiLayout *column = nullptr;
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but;
NodeLinkArg *argN;
int first = 1;
/* generate array of node types sorted by UI name */
- bNodeType **sorted_ntypes = NULL;
- BLI_array_declare(sorted_ntypes);
+ blender::Vector<bNodeType *> sorted_ntypes;
NODE_TYPES_BEGIN (ntype) {
- if (!(ntype->poll && ntype->poll(ntype, ntree))) {
+ const char *disabled_hint;
+ if (!(ntype->poll && ntype->poll(ntype, ntree, &disabled_hint))) {
continue;
}
@@ -493,20 +503,20 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
continue;
}
- BLI_array_append(sorted_ntypes, ntype);
+ sorted_ntypes.append(ntype);
}
NODE_TYPES_END;
qsort(
- sorted_ntypes, BLI_array_len(sorted_ntypes), sizeof(bNodeType *), ui_node_item_name_compare);
+ sorted_ntypes.data(), sorted_ntypes.size(), sizeof(bNodeType *), ui_node_item_name_compare);
/* generate UI */
- for (int j = 0; j < BLI_array_len(sorted_ntypes); j++) {
+ for (int j = 0; j < sorted_ntypes.size(); j++) {
bNodeType *ntype = sorted_ntypes[j];
NodeLinkItem *items;
int totitems;
char name[UI_MAX_NAME_STR];
- const char *cur_node_name = NULL;
+ const char *cur_node_name = nullptr;
int num = 0;
int icon = ICON_NONE;
@@ -526,11 +536,11 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
}
if (first) {
- column = uiLayoutColumn(layout, 0);
+ column = uiLayoutColumn(layout, false);
UI_block_layout_set_current(block, column);
uiItemL(column, IFACE_(cname), ICON_NODE);
- but = block->buttons.last;
+ but = (uiBut *)block->buttons.last;
first = 0;
}
@@ -548,7 +558,7 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
0,
UI_UNIT_X * 4,
UI_UNIT_Y,
- NULL,
+ nullptr,
0.0,
0.0,
0.0,
@@ -573,24 +583,22 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
0,
UI_UNIT_X * 4,
UI_UNIT_Y,
- NULL,
+ nullptr,
0.0,
0.0,
0.0,
0.0,
TIP_("Add node to input"));
- argN = MEM_dupallocN(arg);
+ argN = (NodeLinkArg *)MEM_dupallocN(arg);
argN->item = items[i];
- UI_but_funcN_set(but, ui_node_link, argN, NULL);
+ UI_but_funcN_set(but, ui_node_link, argN, nullptr);
}
if (items) {
MEM_freeN(items);
}
}
-
- BLI_array_free(sorted_ntypes);
}
static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name)
@@ -630,7 +638,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
if (sock->link) {
uiItemL(column, IFACE_("Link"), ICON_NONE);
- but = block->buttons.last;
+ but = (uiBut *)block->buttons.last;
but->drawflag = UI_BUT_TEXT_LEFT;
but = uiDefBut(block,
@@ -641,7 +649,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
0,
UI_UNIT_X * 4,
UI_UNIT_Y,
- NULL,
+ nullptr,
0.0,
0.0,
0.0,
@@ -657,7 +665,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
0,
UI_UNIT_X * 4,
UI_UNIT_Y,
- NULL,
+ nullptr,
0.0,
0.0,
0.0,
@@ -678,7 +686,7 @@ void uiTemplateNodeLink(
uiBut *but;
float socket_col[4];
- arg = MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg");
+ arg = (NodeLinkArg *)MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg");
arg->ntree = ntree;
arg->node = node;
arg->sock = input;
@@ -693,11 +701,11 @@ void uiTemplateNodeLink(
char name[UI_MAX_NAME_STR];
ui_node_sock_name(ntree, input, name);
but = uiDefMenuBut(
- block, ui_template_node_link_menu, NULL, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
+ block, ui_template_node_link_menu, nullptr, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
}
else {
but = uiDefIconMenuBut(
- block, ui_template_node_link_menu, NULL, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
+ block, ui_template_node_link_menu, nullptr, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
}
UI_but_type_set_menu_from_pulldown(but);
@@ -734,7 +742,7 @@ static void ui_node_draw_node(
}
}
- for (input = node->inputs.first; input; input = input->next) {
+ for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
ui_node_draw_input(layout, C, ntree, node, input, depth + 1);
}
}
@@ -744,7 +752,7 @@ static void ui_node_draw_input(
{
PointerRNA inputptr, nodeptr;
uiBlock *block = uiLayoutGetBlock(layout);
- uiLayout *row = NULL;
+ uiLayout *row = nullptr;
bNode *lnode;
bool dependency_loop;
@@ -754,11 +762,11 @@ static void ui_node_draw_input(
/* to avoid eternal loops on cyclic dependencies */
node->flag |= NODE_TEST;
- lnode = (input->link) ? input->link->fromnode : NULL;
+ lnode = (input->link) ? input->link->fromnode : nullptr;
dependency_loop = (lnode && (lnode->flag & NODE_TEST));
if (dependency_loop) {
- lnode = NULL;
+ lnode = nullptr;
}
/* socket RNA pointer */
@@ -841,7 +849,7 @@ static void ui_node_draw_input(
case SOCK_STRING: {
const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
if (node_tree->type == NTREE_GEOMETRY) {
- node_geometry_add_attribute_search_button(node_tree, node, &inputptr, row);
+ node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, row);
}
else {
uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE);
@@ -857,7 +865,7 @@ static void ui_node_draw_input(
}
if (add_dummy_decorator) {
- uiItemDecoratorR(split_wrapper.decorate_column, NULL, NULL, 0);
+ uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0);
}
/* clear */
@@ -874,7 +882,7 @@ void uiTemplateNodeView(
}
/* clear for cycle check */
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) {
tnode->flag &= ~NODE_TEST;
}
diff --git a/source/blender/editors/space_node/node_toolbar.c b/source/blender/editors/space_node/node_toolbar.cc
index 2e7d6ab6cd5..2e7d6ab6cd5 100644
--- a/source/blender/editors/space_node/node_toolbar.c
+++ b/source/blender/editors/space_node/node_toolbar.cc
diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.cc
index 8ecab92aa26..f0db0539c4f 100644
--- a/source/blender/editors/space_node/node_view.c
+++ b/source/blender/editors/space_node/node_view.cc
@@ -76,7 +76,7 @@ int space_node_view_flag(
BLI_rctf_init_minmax(&cur_new);
if (snode->edittree) {
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if ((node->flag & node_flag) == node_flag) {
BLI_rctf_union(&cur_new, &node->totr);
tot++;
@@ -191,16 +191,16 @@ void NODE_OT_view_selected(wmOperatorType *ot)
/** \name Background Image Operators
* \{ */
-typedef struct NodeViewMove {
+struct NodeViewMove {
int mvalo[2];
int xmin, ymin, xmax, ymax;
-} NodeViewMove;
+};
static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
SpaceNode *snode = CTX_wm_space_node(C);
ARegion *region = CTX_wm_region(C);
- NodeViewMove *nvm = op->customdata;
+ NodeViewMove *nvm = (NodeViewMove *)op->customdata;
switch (event->type) {
case MOUSEMOVE:
@@ -215,8 +215,8 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e
CLAMP(snode->yof, nvm->ymin, nvm->ymax);
ED_region_tag_redraw(region);
- WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
- WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+ WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr);
break;
@@ -225,7 +225,7 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e
case RIGHTMOUSE:
if (event->val == KM_RELEASE) {
MEM_freeN(nvm);
- op->customdata = NULL;
+ op->customdata = nullptr;
return OPERATOR_FINISHED;
}
break;
@@ -247,14 +247,14 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
void *lock;
ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
- if (ibuf == NULL) {
+ if (ibuf == nullptr) {
BKE_image_release_ibuf(ima, ibuf, lock);
return OPERATOR_CANCELLED;
}
- nvm = MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct");
+ nvm = (NodeViewMove *)MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct");
op->customdata = nvm;
nvm->mvalo[0] = event->mval[0];
nvm->mvalo[1] = event->mval[1];
@@ -275,7 +275,7 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
static void snode_bg_viewmove_cancel(bContext *UNUSED(C), wmOperator *op)
{
MEM_freeN(op->customdata);
- op->customdata = NULL;
+ op->customdata = nullptr;
}
void NODE_OT_backimage_move(wmOperatorType *ot)
@@ -309,8 +309,8 @@ static int backimage_zoom_exec(bContext *C, wmOperator *op)
snode->zoom *= fac;
ED_region_tag_redraw(region);
- WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
- WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+ WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr);
return OPERATOR_FINISHED;
}
@@ -356,9 +356,9 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op))
float facx, facy;
ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
- if ((ibuf == NULL) || (ibuf->x == 0) || (ibuf->y == 0)) {
+ if ((ibuf == nullptr) || (ibuf->x == 0) || (ibuf->y == 0)) {
BKE_image_release_ibuf(ima, ibuf, lock);
return OPERATOR_CANCELLED;
}
@@ -374,8 +374,8 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op))
snode->yof = 0;
ED_region_tag_redraw(region);
- WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
- WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+ WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr);
return OPERATOR_FINISHED;
}
@@ -402,7 +402,7 @@ void NODE_OT_backimage_fit(wmOperatorType *ot)
/** \name Sample Backdrop Operator
* \{ */
-typedef struct ImageSampleInfo {
+struct ImageSampleInfo {
ARegionType *art;
void *draw_handle;
int x, y;
@@ -420,12 +420,12 @@ typedef struct ImageSampleInfo {
int draw;
int color_manage;
-} ImageSampleInfo;
+};
static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
{
Scene *scene = CTX_data_scene(C);
- ImageSampleInfo *info = arg_info;
+ ImageSampleInfo *info = (ImageSampleInfo *)arg_info;
if (info->draw) {
ED_image_draw_info(scene,
@@ -445,7 +445,7 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
/* Returns mouse position in image space. */
bool ED_space_node_get_position(
- Main *bmain, SpaceNode *snode, struct ARegion *ar, const int mval[2], float fpos[2])
+ Main *bmain, SpaceNode *snode, struct ARegion *region, const int mval[2], float fpos[2])
{
if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) {
return false;
@@ -453,7 +453,7 @@ bool ED_space_node_get_position(
void *lock;
Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (!ibuf) {
BKE_image_release_ibuf(ima, ibuf, lock);
return false;
@@ -462,8 +462,10 @@ bool ED_space_node_get_position(
/* map the mouse coords to the backdrop image space */
float bufx = ibuf->x * snode->zoom;
float bufy = ibuf->y * snode->zoom;
- fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
- fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
+ fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f :
+ 0.0f);
+ fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f :
+ 0.0f);
BKE_image_release_ibuf(ima, ibuf, lock);
return true;
@@ -489,7 +491,7 @@ bool ED_space_node_color_sample(
}
ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (!ibuf) {
return false;
}
@@ -532,14 +534,14 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
ARegion *region = CTX_wm_region(C);
- ImageSampleInfo *info = op->customdata;
+ ImageSampleInfo *info = (ImageSampleInfo *)op->customdata;
void *lock;
Image *ima;
ImBuf *ibuf;
float fx, fy, bufx, bufy;
ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (!ibuf) {
info->draw = 0;
return;
@@ -570,8 +572,8 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
info->draw = 1;
info->channels = ibuf->channels;
- info->zp = NULL;
- info->zfp = NULL;
+ info->zp = nullptr;
+ info->zfp = nullptr;
if (ibuf->rect) {
cp = (uchar *)(ibuf->rect + y * ibuf->x + x);
@@ -616,7 +618,7 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
}
else {
info->draw = 0;
- ED_node_sample_set(NULL);
+ ED_node_sample_set(nullptr);
}
BKE_image_release_ibuf(ima, ibuf, lock);
@@ -626,9 +628,9 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
static void sample_exit(bContext *C, wmOperator *op)
{
- ImageSampleInfo *info = op->customdata;
+ ImageSampleInfo *info = (ImageSampleInfo *)op->customdata;
- ED_node_sample_set(NULL);
+ ED_node_sample_set(nullptr);
ED_region_draw_cb_exit(info->art, info->draw_handle);
ED_area_tag_redraw(CTX_wm_area(C));
MEM_freeN(info);
@@ -644,7 +646,7 @@ static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
+ info = (ImageSampleInfo *)MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
info->art = region->type;
info->draw_handle = ED_region_draw_cb_activate(
region->type, sample_draw, info, REGION_DRAW_POST_PIXEL);
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index 5d14919502e..f20a9409d90 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -74,7 +74,7 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from)
copy_v2_v2(path->view_center, ntree->view_center);
if (id) {
- BLI_strncpy(path->node_name, id->name + 2, sizeof(path->node_name));
+ BLI_strncpy(path->display_name, id->name + 2, sizeof(path->display_name));
}
BLI_addtail(&snode->treepath, path);
@@ -111,6 +111,7 @@ void ED_node_tree_push(SpaceNode *snode, bNodeTree *ntree, bNode *gnode)
}
BLI_strncpy(path->node_name, gnode->name, sizeof(path->node_name));
+ BLI_strncpy(path->display_name, gnode->name, sizeof(path->display_name));
}
else {
path->parent_key = NODE_INSTANCE_KEY_BASE;
@@ -175,7 +176,7 @@ int ED_node_tree_path_length(SpaceNode *snode)
int length = 0;
int i = 0;
LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
- length += strlen(path->node_name);
+ length += strlen(path->display_name);
if (i > 0) {
length += 1; /* for separator char */
}
@@ -190,12 +191,12 @@ void ED_node_tree_path_get(SpaceNode *snode, char *value)
value[0] = '\0';
LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
if (i == 0) {
- strcpy(value, path->node_name);
- value += strlen(path->node_name);
+ strcpy(value, path->display_name);
+ value += strlen(path->display_name);
}
else {
- sprintf(value, "/%s", path->node_name);
- value += strlen(path->node_name) + 1;
+ sprintf(value, "/%s", path->display_name);
+ value += strlen(path->display_name) + 1;
}
}
}
@@ -208,10 +209,10 @@ void ED_node_tree_path_get_fixedbuf(SpaceNode *snode, char *value, int max_lengt
int i = 0;
LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
if (i == 0) {
- size = BLI_strncpy_rlen(value, path->node_name, max_length);
+ size = BLI_strncpy_rlen(value, path->display_name, max_length);
}
else {
- size = BLI_snprintf_rlen(value, max_length, "/%s", path->node_name);
+ size = BLI_snprintf_rlen(value, max_length, "/%s", path->display_name);
}
max_length -= size;
if (max_length <= 0) {
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index 4a1e5c1a12c..c31239f0e9c 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -51,6 +51,7 @@ set(SRC
tree/tree_display_data.cc
tree/tree_display_libraries.cc
tree/tree_display_orphaned.cc
+ tree/tree_display_override_library.cc
tree/tree_display_scenes.cc
tree/tree_display_sequencer.cc
tree/tree_display_view_layer.cc
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index b3b36811411..7d889eed612 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -818,12 +818,7 @@ static bool datastack_drop_are_types_valid(StackDropData *drop_data)
switch (drop_data->drag_tselem->type) {
case TSE_MODIFIER_BASE:
case TSE_MODIFIER:
- if (ob_parent->type == OB_GPENCIL) {
- return ob_dst->type == OB_GPENCIL;
- }
- else if (ob_parent->type != OB_GPENCIL) {
- return ob_dst->type != OB_GPENCIL;
- }
+ return (ob_parent->type == OB_GPENCIL) == (ob_dst->type == OB_GPENCIL);
break;
case TSE_CONSTRAINT_BASE:
case TSE_CONSTRAINT:
@@ -1008,7 +1003,7 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa
drag_te, drop_te, insert_type, &ob->greasepencil_modifiers);
ED_object_gpencil_modifier_move_to_index(reports, ob, drop_data->drag_directdata, index);
}
- else if (ob->type != OB_GPENCIL) {
+ else {
index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->modifiers);
ED_object_modifier_move_to_index(reports, ob, drop_data->drag_directdata, index);
}
@@ -1104,8 +1099,6 @@ static bool collection_drop_init(bContext *C,
const wmEvent *event,
CollectionDrop *data)
{
- SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
-
/* Get collection to drop into. */
TreeElementInsertType insert_type;
TreeElement *te = outliner_drop_insert_collection_find(C, event, &insert_type);
@@ -1140,7 +1133,7 @@ static bool collection_drop_init(bContext *C,
/* Get collection to drag out of. */
ID *parent = drag_id->from_parent;
Collection *from_collection = collection_parent_from_ID(parent);
- if (event->ctrl || space_outliner->outlinevis == SO_SCENES) {
+ if (event->ctrl) {
from_collection = NULL;
}
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 0916e106abf..328a787c768 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -1775,6 +1775,87 @@ static void outliner_draw_userbuts(uiBlock *block,
}
}
+static bool outliner_draw_overrides_buts(uiBlock *block,
+ ARegion *region,
+ SpaceOutliner *space_outliner,
+ ListBase *lb,
+ const bool is_open)
+{
+ bool any_item_has_warnings = false;
+
+ LISTBASE_FOREACH (TreeElement *, te, lb) {
+ bool item_has_warnings = false;
+ const bool do_draw = (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin &&
+ te->ys <= region->v2d.cur.ymax);
+ int but_flag = UI_BUT_DRAG_LOCK;
+ const char *tip = NULL;
+
+ TreeStoreElem *tselem = TREESTORE(te);
+ switch (tselem->type) {
+ case TSE_LIBRARY_OVERRIDE_BASE: {
+ ID *id = tselem->id;
+
+ if (id->flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) {
+ item_has_warnings = true;
+ if (do_draw) {
+ tip = TIP_(
+ "This override data-block is not needed anymore, but was detected as user-edited");
+ }
+ }
+ else if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && ID_REAL_USERS(id) == 0) {
+ item_has_warnings = true;
+ if (do_draw) {
+ tip = TIP_("This override data-block is unused");
+ }
+ }
+ break;
+ }
+ case TSE_LIBRARY_OVERRIDE: {
+ const bool is_rna_path_valid = (bool)(POINTER_AS_UINT(te->directdata));
+ if (!is_rna_path_valid) {
+ item_has_warnings = true;
+ if (do_draw) {
+ tip = TIP_(
+ "This override property does not exist in current data, it will be removed on "
+ "next .blend file save");
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ const bool any_child_has_warnings = outliner_draw_overrides_buts(
+ block,
+ region,
+ space_outliner,
+ &te->subtree,
+ is_open && TSELEM_OPEN(tselem, space_outliner));
+
+ if (do_draw &&
+ (item_has_warnings || (any_child_has_warnings && !TSELEM_OPEN(tselem, space_outliner)))) {
+ if (tip == NULL) {
+ tip = TIP_("Some sub-items require attention");
+ }
+ uiBut *bt = uiDefIconBlockBut(block,
+ NULL,
+ NULL,
+ 1,
+ ICON_ERROR,
+ (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS),
+ te->ys,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ tip);
+ UI_but_flag_enable(bt, but_flag);
+ }
+ any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings;
+ }
+
+ return any_item_has_warnings;
+}
+
static void outliner_draw_rnacols(ARegion *region, int sizex)
{
View2D *v2d = &region->v2d;
@@ -2025,7 +2106,7 @@ static void outliner_draw_mode_column_toggle(uiBlock *block,
tip);
UI_but_func_set(but, outliner_mode_toggle_fn, tselem, NULL);
UI_but_flag_enable(but, UI_BUT_DRAG_LOCK);
- /* Mode toggling handles it's own undo state because undo steps need to be grouped. */
+ /* Mode toggling handles its own undo state because undo steps need to be grouped. */
UI_but_flag_disable(but, UI_BUT_UNDO);
if (ID_IS_LINKED(&ob->id)) {
@@ -2896,7 +2977,19 @@ static void outliner_draw_iconrow(bContext *C,
active = tree_element_type_active_state_get(C, tvc, te, tselem);
}
- if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION, TSE_R_LAYER, TSE_GP_LAYER)) {
+ if (!ELEM(tselem->type,
+ TSE_ID_BASE,
+ TSE_SOME_ID,
+ TSE_LAYER_COLLECTION,
+ TSE_R_LAYER,
+ TSE_GP_LAYER,
+ TSE_LIBRARY_OVERRIDE_BASE,
+ TSE_LIBRARY_OVERRIDE,
+ TSE_BONE,
+ TSE_EBONE,
+ TSE_POSE_CHANNEL,
+ TSE_POSEGRP,
+ TSE_DEFGROUP)) {
outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1);
}
else {
@@ -3656,7 +3749,11 @@ void draw_outliner(const bContext *C)
}
/* Sync selection state from view layer. */
- if (!ELEM(space_outliner->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS) &&
+ if (!ELEM(space_outliner->outlinevis,
+ SO_LIBRARIES,
+ SO_OVERRIDES_LIBRARY,
+ SO_DATA_API,
+ SO_ID_ORPHANS) &&
space_outliner->flag & SO_SYNC_SELECT) {
outliner_sync_selection(C, space_outliner);
}
@@ -3703,6 +3800,10 @@ void draw_outliner(const bContext *C)
/* draw user toggle columns */
outliner_draw_userbuts(block, region, space_outliner, &space_outliner->tree);
}
+ else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) {
+ /* Draw overrides status columns. */
+ outliner_draw_overrides_buts(block, region, space_outliner, &space_outliner->tree, true);
+ }
else if (restrict_column_width > 0.0f) {
/* draw restriction columns */
RestrictPropertiesActive props_active;
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 681f7fab18a..4a070590d55 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -154,7 +154,9 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot)
/** \name Toggle Open/Closed Operator
* \{ */
-/* Open or close a tree element, optionally toggling all children recursively */
+/**
+ * Open or close a tree element, optionally toggling all children recursively.
+ */
void outliner_item_openclose(SpaceOutliner *space_outliner,
TreeElement *te,
bool open,
@@ -466,7 +468,7 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto
BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked id '%s'", id->name);
return;
}
- if (BKE_library_ID_is_indirectly_used(bmain, id) && ID_REAL_USERS(id) <= 1) {
+ if (ID_REAL_USERS(id) <= 1 && BKE_library_ID_is_indirectly_used(bmain, id)) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete id '%s', indirectly used data-blocks need at least one user",
@@ -665,6 +667,10 @@ static const EnumPropertyItem *outliner_id_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return DummyRNA_NULL_items;
+ }
+
EnumPropertyItem item_tmp = {0}, *item = NULL;
int totitem = 0;
int i = 0;
@@ -1595,8 +1601,10 @@ void OUTLINER_OT_show_one_level(wmOperatorType *ot)
/** \name Show Hierarchy Operator
* \{ */
-/* Helper function for tree_element_shwo_hierarchy() -
- * recursively checks whether subtrees have any objects. */
+/**
+ * Helper function for #tree_element_shwo_hierarchy() -
+ * recursively checks whether subtrees have any objects.
+ */
static int subtree_has_objects(ListBase *lb)
{
LISTBASE_FOREACH (TreeElement *, te, lb) {
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index f65e273c1b5..fea5ddae16b 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -193,7 +193,7 @@ typedef enum {
/* The outliner display modes that support the filter system.
* Note: keep it synced with space_outliner.py */
#define SUPPORT_FILTER_OUTLINER(space_outliner_) \
- (ELEM((space_outliner_)->outlinevis, SO_VIEW_LAYER))
+ (ELEM((space_outliner_)->outlinevis, SO_VIEW_LAYER, SO_OVERRIDES_LIBRARY))
/* Outliner Searching --
*
diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c
index 6543a909a41..d78767019b5 100644
--- a/source/blender/editors/space_outliner/outliner_sync.c
+++ b/source/blender/editors/space_outliner/outliner_sync.c
@@ -356,8 +356,11 @@ static void outliner_sync_selection_from_outliner(Scene *scene,
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
{
/* Don't sync if not checked or in certain outliner display modes */
- if (!(space_outliner->flag & SO_SYNC_SELECT) ||
- ELEM(space_outliner->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS)) {
+ if (!(space_outliner->flag & SO_SYNC_SELECT) || ELEM(space_outliner->outlinevis,
+ SO_LIBRARIES,
+ SO_OVERRIDES_LIBRARY,
+ SO_DATA_API,
+ SO_ID_ORPHANS)) {
return;
}
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index dc9205106ab..f809bb13b42 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -598,8 +598,14 @@ static uiBlock *merged_element_search_menu(bContext *C, ARegion *region, void *d
short menu_width = 10 * UI_UNIT_X;
but = uiDefSearchBut(
block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, "");
- UI_but_func_search_set(
- but, NULL, merged_element_search_update_fn, data, NULL, merged_element_search_exec_fn, NULL);
+ UI_but_func_search_set(but,
+ NULL,
+ merged_element_search_update_fn,
+ data,
+ false,
+ NULL,
+ merged_element_search_exec_fn,
+ NULL);
UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
/* Fake button to hold space for search items */
@@ -699,8 +705,8 @@ static void outliner_object_delete_fn(bContext *C, ReportList *reports, Scene *s
reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", ob->id.name + 2);
return;
}
- if (BKE_library_ID_is_indirectly_used(bmain, ob) && ID_REAL_USERS(ob) <= 1 &&
- ID_EXTRA_USERS(ob) == 0) {
+ if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, ob)) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at "
@@ -734,7 +740,7 @@ static void id_local_fn(bContext *C,
BKE_lib_id_clear_library_data(bmain, tselem->id);
}
else {
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
}
}
else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) {
@@ -840,13 +846,13 @@ static void id_override_library_create_fn(bContext *C,
te->store_elem->id->tag |= LIB_TAG_DOIT;
}
success = BKE_lib_override_library_create(
- bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference);
+ bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, NULL);
}
else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != NULL;
/* Cleanup. */
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
@@ -896,7 +902,7 @@ static void id_override_library_reset_fn(bContext *C,
}
static void id_override_library_resync_fn(bContext *C,
- ReportList *UNUSED(reports),
+ ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *UNUSED(tsep),
@@ -925,7 +931,7 @@ static void id_override_library_resync_fn(bContext *C,
}
BKE_lib_override_library_resync(
- bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce);
+ bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true, reports);
WM_event_add_notifier(C, NC_WINDOW, NULL);
}
@@ -1422,8 +1428,8 @@ static Base *outline_batch_delete_hierarchy(
base->object->id.name + 2);
return base_next;
}
- if (BKE_library_ID_is_indirectly_used(bmain, object) && ID_REAL_USERS(object) <= 1 &&
- ID_EXTRA_USERS(object) == 0) {
+ if (ID_REAL_USERS(object) <= 1 && ID_EXTRA_USERS(object) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, object)) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at least "
@@ -1873,7 +1879,7 @@ static bool outliner_id_operation_item_poll(bContext *C,
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY:
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE:
case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY:
- if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) {
return true;
}
return false;
@@ -2263,7 +2269,8 @@ static const EnumPropertyItem outliner_lib_op_type_items[] = {
"DELETE",
ICON_X,
"Delete",
- "Delete this library and all its item from Blender - WARNING: no undo"},
+ "Delete this library and all its item.\n"
+ "Warning: No undo"},
{OL_LIB_RELOCATE,
"RELOCATE",
0,
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index 573fb492613..90389fc1be2 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -908,6 +908,11 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
BLI_assert(!"Expected this ID type to be ported to new Outliner tree-element design");
}
}
+ else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) {
+ if (!te->type) {
+ BLI_assert(!"Expected override types to be ported to new Outliner tree-element design");
+ }
+ }
else {
/* Other cases must be caught above. */
BLI_assert(TSE_IS_REAL_ID(tselem));
@@ -1335,7 +1340,7 @@ static void outliner_sort(ListBase *lb)
tp->name = te->name;
tp->idcode = te->idcode;
- if ((tselem->type != TSE_SOME_ID) && tselem->type != TSE_DEFGROUP) {
+ if (!ELEM(tselem->type, TSE_SOME_ID, TSE_DEFGROUP)) {
tp->idcode = 0; /* Don't sort this. */
}
if (tselem->type == TSE_ID_BASE) {
@@ -1920,5 +1925,5 @@ void outliner_build_tree(Main *mainvar,
outliner_filter_tree(space_outliner, view_layer);
outliner_restore_scrolling_position(space_outliner, region, &focus);
- BKE_main_id_clear_newpoins(mainvar);
+ BKE_main_id_newptr_and_tag_clear(mainvar);
}
diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c
index 562457c62e9..5feb157bfc8 100644
--- a/source/blender/editors/space_outliner/outliner_utils.c
+++ b/source/blender/editors/space_outliner/outliner_utils.c
@@ -361,6 +361,7 @@ float outliner_restrict_columns_width(const SpaceOutliner *space_outliner)
case SO_DATA_API:
case SO_SEQUENCE:
case SO_LIBRARIES:
+ case SO_OVERRIDES_LIBRARY:
return 0.0f;
case SO_ID_ORPHANS:
num_columns = 3;
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index 419713035b6..728be1ccaaf 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -155,6 +155,8 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params)
case NC_OBJECT:
switch (wmn->data) {
case ND_TRANSFORM:
+ ED_region_tag_redraw_no_rebuild(region);
+ break;
case ND_BONE_ACTIVE:
case ND_BONE_SELECT:
case ND_DRAW:
@@ -273,7 +275,7 @@ static void outliner_main_region_message_subscribe(const wmRegionMessageSubscrib
.notify = ED_region_do_msg_notify_tag_redraw,
};
- if (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)) {
+ if (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES, SO_OVERRIDES_LIBRARY)) {
WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw);
}
}
diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc
index 4395383e838..003afd5bdec 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display.cc
@@ -44,6 +44,9 @@ TreeDisplay *outliner_tree_display_create(eSpaceOutliner_Mode mode, SpaceOutline
case SO_ID_ORPHANS:
tree_display = new TreeDisplayIDOrphans(*space_outliner);
break;
+ case SO_OVERRIDES_LIBRARY:
+ tree_display = new TreeDisplayOverrideLibrary(*space_outliner);
+ break;
case SO_VIEW_LAYER:
default:
tree_display = new TreeDisplayViewLayer(*space_outliner);
diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh
index b6183050e82..f089a149805 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.hh
+++ b/source/blender/editors/space_outliner/tree/tree_display.hh
@@ -111,6 +111,24 @@ class TreeDisplayLibraries final : public AbstractTreeDisplay {
};
/* -------------------------------------------------------------------- */
+/* Library Overrides Tree-Display. */
+
+/**
+ * \brief Tree-Display for the Library Overrides display mode.
+ */
+class TreeDisplayOverrideLibrary final : public AbstractTreeDisplay {
+ public:
+ TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner);
+
+ ListBase buildTree(const TreeSourceData &source_data) override;
+
+ private:
+ TreeElement *add_library_contents(Main &, ListBase &, Library *) const;
+ bool override_library_id_filter_poll(Library *lib, ID *id) const;
+ short id_filter_get() const;
+};
+
+/* -------------------------------------------------------------------- */
/* Video Sequencer Tree-Display */
enum SequenceAddOp {
diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
new file mode 100644
index 00000000000..3059f8bfe0c
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
@@ -0,0 +1,204 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include "BLI_listbase.h"
+#include "BLI_listbase_wrapper.hh"
+
+#include "BKE_collection.h"
+#include "BKE_main.h"
+
+#include "DNA_collection_types.h"
+
+#include "BLT_translation.h"
+
+#include "../outliner_intern.h"
+#include "tree_display.hh"
+
+namespace blender::ed::outliner {
+
+/* Convenience/readability. */
+template<typename T> using List = ListBaseWrapper<T>;
+
+TreeDisplayOverrideLibrary::TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner)
+ : AbstractTreeDisplay(space_outliner)
+{
+}
+
+ListBase TreeDisplayOverrideLibrary::buildTree(const TreeSourceData &source_data)
+{
+ ListBase tree = {nullptr};
+
+ {
+ /* current file first - mainvar provides tselem with unique pointer - not used */
+ TreeElement *ten = add_library_contents(*source_data.bmain, tree, nullptr);
+ TreeStoreElem *tselem;
+
+ if (ten) {
+ tselem = TREESTORE(ten);
+ if (!tselem->used) {
+ tselem->flag &= ~TSE_CLOSED;
+ }
+ }
+ }
+
+ for (ID *id : List<ID>(source_data.bmain->libraries)) {
+ Library *lib = reinterpret_cast<Library *>(id);
+ TreeElement *ten = add_library_contents(*source_data.bmain, tree, lib);
+ /* NULL-check matters, due to filtering there may not be a new element. */
+ if (ten) {
+ lib->id.newid = (ID *)ten;
+ }
+ }
+
+ /* make hierarchy */
+ for (TreeElement *ten : List<TreeElement>(tree)) {
+ if (ten == tree.first) {
+ /* First item is main, skip. */
+ continue;
+ }
+
+ TreeStoreElem *tselem = TREESTORE(ten);
+ Library *lib = (Library *)tselem->id;
+ BLI_assert(!lib || (GS(lib->id.name) == ID_LI));
+ if (!lib || !lib->parent) {
+ continue;
+ }
+
+ TreeElement *parent = (TreeElement *)lib->parent->id.newid;
+
+ if (tselem->id->tag & LIB_TAG_INDIRECT) {
+ /* Only remove from 'first level' if lib is not also directly used. */
+ BLI_remlink(&tree, ten);
+ BLI_addtail(&parent->subtree, ten);
+ ten->parent = parent;
+ }
+ else {
+ /* Else, make a new copy of the libtree for our parent. */
+ TreeElement *dupten = add_library_contents(*source_data.bmain, parent->subtree, lib);
+ if (dupten) {
+ dupten->parent = parent;
+ }
+ }
+ }
+ /* restore newid pointers */
+ for (ID *library_id : List<ID>(source_data.bmain->libraries)) {
+ library_id->newid = nullptr;
+ }
+
+ return tree;
+}
+
+TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar,
+ ListBase &lb,
+ Library *lib) const
+{
+ const short filter_id_type = id_filter_get();
+
+ ListBase *lbarray[INDEX_ID_MAX];
+ int tot;
+ if (filter_id_type) {
+ lbarray[0] = which_libbase(&mainvar, space_outliner_.filter_id_type);
+ tot = 1;
+ }
+ else {
+ tot = set_listbasepointers(&mainvar, lbarray);
+ }
+
+ TreeElement *tenlib = nullptr;
+ for (int a = 0; a < tot; a++) {
+ if (!lbarray[a] || !lbarray[a]->first) {
+ continue;
+ }
+
+ ID *id = nullptr;
+
+ /* check if there's data in current lib */
+ for (ID *id_iter : List<ID>(lbarray[a])) {
+ if (id_iter->lib == lib && ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
+ id = id_iter;
+ break;
+ }
+ }
+
+ if (id != nullptr) {
+ if (!tenlib) {
+ /* Create library tree element on demand, depending if there are any data-blocks. */
+ if (lib) {
+ tenlib = outliner_add_element(&space_outliner_, &lb, lib, nullptr, TSE_SOME_ID, 0);
+ }
+ else {
+ tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0);
+ tenlib->name = IFACE_("Current File");
+ }
+ }
+
+ /* Create data-block list parent element on demand. */
+ if (id != nullptr) {
+ TreeElement *ten;
+
+ if (filter_id_type) {
+ ten = tenlib;
+ }
+ else {
+ ten = outliner_add_element(
+ &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
+ ten->directdata = lbarray[a];
+ ten->name = outliner_idcode_to_plural(GS(id->name));
+ }
+
+ for (ID *id : List<ID>(lbarray[a])) {
+ if (override_library_id_filter_poll(lib, id)) {
+ TreeElement *override_tree_element = outliner_add_element(
+ &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0);
+
+ if (BLI_listbase_is_empty(&override_tree_element->subtree)) {
+ outliner_free_tree_element(override_tree_element, &ten->subtree);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return tenlib;
+}
+
+short TreeDisplayOverrideLibrary::id_filter_get() const
+{
+ if (space_outliner_.filter & SO_FILTER_ID_TYPE) {
+ return space_outliner_.filter_id_type;
+ }
+ return 0;
+}
+
+bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(Library *lib, ID *id) const
+{
+ if (id->lib != lib) {
+ return false;
+ }
+
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
index 6b222e877b1..c5d254242c6 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
@@ -40,13 +40,24 @@ TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &i
: AbstractTreeElement(legacy_te), id_(id)
{
BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_BASE);
- legacy_te.name = IFACE_("Library Overrides");
+ if (legacy_te.parent != nullptr &&
+ ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION))
+
+ {
+ legacy_te.name = IFACE_("Library Overrides");
+ }
+ else {
+ legacy_te.name = id.name + 2;
+ }
}
void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
{
BLI_assert(id_.override_library != nullptr);
+ const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) &&
+ (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) !=
+ 0);
PointerRNA idpoin;
RNA_id_pointer_create(&id_, &idpoin);
@@ -56,15 +67,26 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
for (auto *override_prop :
ListBaseWrapper<IDOverrideLibraryProperty>(id_.override_library->properties)) {
- if (!BKE_lib_override_rna_property_find(
- &idpoin, override_prop, &override_rna_ptr, &override_rna_prop)) {
- /* This is fine, override properties list is not always fully up-to-date with current
- * RNA/IDProps etc., this gets cleaned up when re-generating the overrides rules,
- * no error here. */
- continue;
+ const bool is_rna_path_valid = BKE_lib_override_rna_property_find(
+ &idpoin, override_prop, &override_rna_ptr, &override_rna_prop);
+ if (is_rna_path_valid && !show_system_overrides &&
+ ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) &&
+ RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) {
+ bool do_continue = true;
+ for (auto *override_prop_op :
+ ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) {
+ if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
+ do_continue = false;
+ break;
+ }
+ }
+
+ if (do_continue) {
+ continue;
+ }
}
- TreeElementOverridesData data = {id_, *override_prop};
+ TreeElementOverridesData data = {id_, *override_prop, is_rna_path_valid};
outliner_add_element(
&space_outliner, &legacy_te_.subtree, &data, &legacy_te_, TSE_LIBRARY_OVERRIDE, index++);
}
@@ -79,6 +101,9 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t
BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE);
legacy_te.name = override_prop_.rna_path;
+ /* Abusing this for now, better way to do it is also pending current refactor of the whole tree
+ * code to use C++. */
+ legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid);
}
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
index b5c772f5b33..c3caab8e268 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
@@ -27,6 +27,7 @@ namespace blender::ed::outliner {
struct TreeElementOverridesData {
ID &id;
IDOverrideLibraryProperty &override_property;
+ bool is_rna_path_valid;
};
class TreeElementOverridesBase final : public AbstractTreeElement {
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index a6e7903d1b1..ac31e0e7c37 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -95,6 +95,7 @@ typedef struct SequencerAddData {
#define SEQPROP_NOPATHS (1 << 2)
#define SEQPROP_NOCHAN (1 << 3)
#define SEQPROP_FIT_METHOD (1 << 4)
+#define SEQPROP_VIEW_TRANSFORM (1 << 5)
static const EnumPropertyItem scale_fit_methods[] = {
{SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"},
@@ -152,6 +153,14 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
"Fit Method",
"Scale fit method");
}
+
+ if (flag & SEQPROP_VIEW_TRANSFORM) {
+ ot->prop = RNA_def_boolean(ot->srna,
+ "set_view_transform",
+ true,
+ "Set View Transform",
+ "Set appropriate view transform based on media colorspace");
+ }
}
static void sequencer_generic_invoke_path__internal(bContext *C,
@@ -228,7 +237,6 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm
PropertyRNA *prop;
const bool relative = (prop = RNA_struct_find_property(op->ptr, "relative_path")) &&
RNA_property_boolean_get(op->ptr, prop);
- int is_file = -1;
memset(load_data, 0, sizeof(SeqLoadData));
load_data->start_frame = RNA_int_get(op->ptr, "frame_start");
@@ -242,17 +250,26 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm
}
if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
- /* Full path, file is set by the caller. */
RNA_property_string_get(op->ptr, prop, load_data->path);
- is_file = 1;
+ BLI_strncpy(load_data->name, BLI_path_basename(load_data->path), sizeof(load_data->name));
}
else if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
- /* Full path, file is set by the caller. */
- RNA_property_string_get(op->ptr, prop, load_data->path);
- is_file = 0;
+ char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0);
+
+ if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
+ RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
+ char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
+ BLI_strncpy(load_data->name, filename, sizeof(load_data->name));
+ BLI_snprintf(load_data->path, sizeof(load_data->path), "%s%s", directory, filename);
+ MEM_freeN(filename);
+ break;
+ }
+ RNA_PROP_END;
+ }
+ MEM_freeN(directory);
}
- if ((is_file != -1) && relative) {
+ if (relative) {
BLI_path_rel(load_data->path, BKE_main_blendfile_path(bmain));
}
@@ -276,17 +293,9 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm
load_data->flags |= SEQ_LOAD_MOVIE_SYNC_FPS;
}
- if (is_file == 1) {
- BLI_strncpy(load_data->name, BLI_path_basename(load_data->path), sizeof(load_data->name));
- }
- else if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
- RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
- char *name = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
- BLI_strncpy(load_data->name, name, sizeof(load_data->name));
- MEM_freeN(name);
- break;
- }
- RNA_PROP_END;
+ if ((prop = RNA_struct_find_property(op->ptr, "set_view_transform")) &&
+ RNA_property_boolean_get(op->ptr, prop)) {
+ load_data->flags |= SEQ_LOAD_SET_VIEW_TRANSFORM;
}
if ((prop = RNA_struct_find_property(op->ptr, "use_multiview")) &&
@@ -373,8 +382,23 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void sequencer_disable_one_time_properties(bContext *C, wmOperator *op)
+{
+ Editing *ed = SEQ_editing_get(CTX_data_scene(C), false);
+ /* Disable following properties if there are any existing strips, unless overridden by user. */
+ if (ed && ed->seqbasep && ed->seqbasep->first) {
+ if (RNA_struct_find_property(op->ptr, "use_framerate")) {
+ RNA_boolean_set(op->ptr, "use_framerate", false);
+ }
+ if (RNA_struct_find_property(op->ptr, "set_view_transform")) {
+ RNA_boolean_set(op->ptr, "set_view_transform", false);
+ }
+ }
+}
+
static int sequencer_add_scene_strip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
+ sequencer_disable_one_time_properties(C, op);
if (!RNA_struct_property_is_set(op->ptr, "scene")) {
return WM_enum_search_invoke(C, op, event);
}
@@ -707,13 +731,9 @@ static int sequencer_add_movie_strip_invoke(bContext *C,
{
PropertyRNA *prop;
Scene *scene = CTX_data_scene(C);
- Editing *ed = SEQ_editing_get(scene, false);
- /* Only enable "use_framerate" if there aren't any existing strips, unless overridden by user.
- */
- if (ed && ed->seqbasep && ed->seqbasep->first) {
- RNA_boolean_set(op->ptr, "use_framerate", false);
- }
+ sequencer_disable_one_time_properties(C, op);
+
RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene));
/* This is for drag and drop. */
@@ -739,12 +759,11 @@ static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op)
uiLayout *layout = op->layout;
SequencerAddData *sad = op->customdata;
ImageFormatData *imf = &sad->im_format;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
/* Main draw call. */
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* Image template. */
RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
@@ -781,7 +800,8 @@ 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 | SEQPROP_FIT_METHOD);
+ sequencer_generic_props__internal(
+ ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD | SEQPROP_VIEW_TRANSFORM);
RNA_def_boolean(ot->srna, "sound", true, "Sound", "Load sound with the movie");
RNA_def_boolean(ot->srna,
"use_framerate",
@@ -990,8 +1010,10 @@ static void sequencer_add_image_strip_load_files(
wmOperator *op, Sequence *seq, SeqLoadData *load_data, const int minframe, const int numdigits)
{
const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders");
-
- SEQ_add_image_set_directory(seq, load_data->path);
+ /* size of Strip->dir. */
+ char directory[768];
+ BLI_split_dir_part(load_data->path, directory, sizeof(directory));
+ SEQ_add_image_set_directory(seq, directory);
if (use_placeholders) {
sequencer_image_seq_reserve_frames(
@@ -1057,8 +1079,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);
+ sequencer_disable_one_time_properties(C, op);
+
+ RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene));
/* Name set already by drag and drop. */
if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) {
@@ -1104,8 +1127,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 | SEQPROP_FIT_METHOD);
+ sequencer_generic_props__internal(
+ ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD | SEQPROP_VIEW_TRANSFORM);
RNA_def_boolean(ot->srna,
"use_placeholders",
diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c
index 11614d94862..1e0ecfd890e 100644
--- a/source/blender/editors/space_sequencer/sequencer_buttons.c
+++ b/source/blender/editors/space_sequencer/sequencer_buttons.c
@@ -111,10 +111,10 @@ void sequencer_buttons_register(ARegionType *art)
pt = MEM_callocN(sizeof(PanelType), "spacetype sequencer panel metadata");
strcpy(pt->idname, "SEQUENCER_PT_metadata");
strcpy(pt->label, N_("Metadata"));
+ strcpy(pt->category, "Metadata");
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
pt->poll = metadata_panel_context_poll;
pt->draw = metadata_panel_context_draw;
- pt->flag |= PANEL_TYPE_DEFAULT_CLOSED;
pt->order = 10;
BLI_addtail(&art->paneltypes, pt);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index e4afb27dd2e..5f831cbf535 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -105,8 +105,6 @@
* it messes up transform. */
#undef SEQ_ALL_BEGIN
#undef SEQ_ALL_END
-#undef SEQ_CURRENT_BEGIN
-#undef SEQ_CURRENT_END
static Sequence *special_seq_update = NULL;
@@ -872,9 +870,9 @@ static void draw_seq_background(Scene *scene,
/* Draw the main strip body. */
if (is_single_image) {
immRectf(pos,
- SEQ_transform_get_left_handle_frame(seq, false),
+ SEQ_transform_get_left_handle_frame(seq),
y1,
- SEQ_transform_get_right_handle_frame(seq, false),
+ SEQ_transform_get_right_handle_frame(seq),
y2);
}
else {
@@ -887,10 +885,12 @@ static void draw_seq_background(Scene *scene,
immUniformColor4ubv(col);
if (seq->startstill) {
- immRectf(pos, seq->startdisp, y1, (float)(seq->start), y2);
+ const float content_start = min_ff(seq->enddisp, seq->start);
+ immRectf(pos, seq->startdisp, y1, content_start, y2);
}
if (seq->endstill) {
- immRectf(pos, (float)(seq->start + seq->len), y1, seq->enddisp, y2);
+ const float content_end = max_ff(seq->startdisp, seq->start + seq->len);
+ immRectf(pos, content_end, y1, seq->enddisp, y2);
}
}
@@ -1107,6 +1107,10 @@ static void draw_seq_strip(const bContext *C,
x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp;
y2 = seq->machine + SEQ_STRIP_OFSTOP;
+ /* Limit body to strip bounds. Meta strip can end up with content outside of strip range. */
+ x1 = min_ff(x1, seq->enddisp);
+ x2 = max_ff(x2, seq->startdisp);
+
float text_margin_y;
bool y_threshold;
if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) ||
@@ -1524,10 +1528,10 @@ static void *sequencer_OCIO_transform_ibuf(const bContext *C,
ImBuf *ibuf,
bool *r_glsl_used,
eGPUTextureFormat *r_format,
- eGPUDataFormat *r_data)
+ eGPUDataFormat *r_data,
+ void **r_buffer_cache_handle)
{
void *display_buffer;
- void *cache_handle = NULL;
bool force_fallback = false;
*r_glsl_used = false;
force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
@@ -1580,13 +1584,10 @@ static void *sequencer_OCIO_transform_ibuf(const bContext *C,
/* There is data to be displayed, but GLSL is not initialized
* properly, in this case we fallback to CPU-based display transform. */
if ((ibuf->rect || ibuf->rect_float) && !*r_glsl_used) {
- display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle);
+ display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, r_buffer_cache_handle);
*r_format = GPU_RGBA8;
*r_data = GPU_DATA_UBYTE;
}
- if (cache_handle) {
- IMB_display_buffer_release(cache_handle);
- }
return display_buffer;
}
@@ -1660,6 +1661,7 @@ static void sequencer_draw_display_buffer(const bContext *C,
bool draw_backdrop)
{
void *display_buffer;
+ void *buffer_cache_handle = NULL;
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) {
GPU_blend(GPU_BLEND_ALPHA);
@@ -1687,7 +1689,8 @@ static void sequencer_draw_display_buffer(const bContext *C,
data = GPU_DATA_UBYTE;
}
else {
- display_buffer = sequencer_OCIO_transform_ibuf(C, ibuf, &glsl_used, &format, &data);
+ display_buffer = sequencer_OCIO_transform_ibuf(
+ C, ibuf, &glsl_used, &format, &data, &buffer_cache_handle);
}
if (draw_backdrop) {
@@ -1747,6 +1750,10 @@ static void sequencer_draw_display_buffer(const bContext *C,
IMB_colormanagement_finish_glsl_draw();
}
+ if (buffer_cache_handle) {
+ IMB_display_buffer_release(buffer_cache_handle);
+ }
+
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) {
GPU_blend(GPU_BLEND_NONE);
}
@@ -2358,6 +2365,31 @@ static void draw_cache_view(const bContext *C)
}
/* Draw sequencer timeline. */
+static void draw_overlap_frame_indicator(const struct Scene *scene, const View2D *v2d)
+{
+ int overlap_frame = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ?
+ scene->ed->over_cfra :
+ scene->r.cfra + scene->ed->over_ofs;
+
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ float viewport_size[4];
+ GPU_viewport_size_get_f(viewport_size);
+ immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
+ /* Shader may have color set from past usage - reset it. */
+ immUniform1i("colors_len", 0);
+ immUniform1f("dash_width", 20.0f * U.pixelsize);
+ immUniform1f("dash_factor", 0.5f);
+ immUniformThemeColor(TH_CFRAME);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex2f(pos, overlap_frame, v2d->cur.ymin);
+ immVertex2f(pos, overlap_frame, v2d->cur.ymax);
+ immEnd();
+
+ immUnbindProgram();
+}
+
void draw_timeline_seq(const bContext *C, ARegion *region)
{
Scene *scene = CTX_data_scene(C);
@@ -2417,31 +2449,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
cfra_flag |= DRAWCFRA_UNIT_SECONDS;
}
- /* Draw overlap frame frame indicator. */
- if (scene->ed && scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) {
- int overlap_frame = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ?
- scene->ed->over_cfra :
- scene->r.cfra + scene->ed->over_ofs;
-
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
- float viewport_size[4];
- GPU_viewport_size_get_f(viewport_size);
- immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
- /* Shader may have color set from past usage - reset it. */
- immUniform1i("colors_len", 0);
- immUniform1f("dash_width", 20.0f * U.pixelsize);
- immUniform1f("dash_factor", 0.5f);
- immUniformThemeColor(TH_CFRAME);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex2f(pos, overlap_frame, v2d->cur.ymin);
- immVertex2f(pos, overlap_frame, v2d->cur.ymax);
- immEnd();
-
- immUnbindProgram();
- }
-
UI_view2d_view_orthoSpecial(region, v2d, 1);
int marker_draw_flag = DRAW_MARKERS_MARGIN;
if (sseq->flag & SEQ_SHOW_MARKERS) {
@@ -2449,11 +2456,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
}
UI_view2d_view_ortho(v2d);
-
- if (ed) {
- draw_cache_view(C);
- }
-
ANIM_draw_previewrange(C, v2d, 1);
/* Draw registered callbacks. */
@@ -2479,6 +2481,13 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region)
const SpaceSeq *sseq = CTX_wm_space_seq(C);
View2D *v2d = &region->v2d;
+ if (scene->ed && scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) {
+ UI_view2d_view_ortho(v2d);
+ draw_cache_view(C);
+ draw_overlap_frame_indicator(scene, v2d);
+ UI_view2d_view_restore(C);
+ }
+
ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true);
UI_view2d_scrollers_draw(v2d, NULL);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 78d263dffad..1c9b3676b19 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -34,6 +34,7 @@
#include "BLT_translation.h"
+#include "DNA_anim_types.h"
#include "DNA_scene_types.h"
#include "DNA_sound_types.h"
@@ -138,6 +139,42 @@ bool ED_space_sequencer_check_show_strip(SpaceSeq *sseq)
ELEM(sseq->mainb, SEQ_DRAW_SEQUENCE, SEQ_DRAW_IMG_IMBUF));
}
+static bool sequencer_fcurves_targets_color_strip(const FCurve *fcurve)
+{
+ if (!BLI_str_startswith(fcurve->rna_path, "sequence_editor.sequences_all[\"")) {
+ return false;
+ }
+
+ if (!BLI_str_endswith(fcurve->rna_path, "\"].color")) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Check if there is animation attached to a strip, that is shown on the strip in the UI.
+ *
+ * - Colors of color strips are displayed on the strip itself.
+ */
+bool ED_space_sequencer_has_visible_animation_on_strip(const struct Scene *scene)
+{
+ if (!scene->adt) {
+ return false;
+ }
+ if (!scene->adt->action) {
+ return false;
+ }
+
+ LISTBASE_FOREACH (FCurve *, fcurve, &scene->adt->action->curves) {
+ if (sequencer_fcurves_targets_color_strip(fcurve)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -290,7 +327,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
snap_frame = RNA_int_get(op->ptr, "frame");
- /* Check metas. */
+ /* Check meta-strips. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK) &&
SEQ_transform_sequence_can_be_translated(seq)) {
@@ -312,8 +349,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
}
}
- /* Test for effects and overlap.
- * Don't use SEQ_CURRENT_BEGIN since that would be recursive. */
+ /* Test for effects and overlap. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK)) {
seq->flag &= ~SEQ_OVERLAP;
@@ -349,7 +385,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -558,7 +594,7 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset)
seq->endofs = endframe - seq->enddisp;
changed = true;
}
- else if (endframe <= seq->enddisp) {
+ else {
seq->endstill = seq->enddisp - endframe;
seq->endofs = 0;
changed = true;
@@ -569,7 +605,7 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset)
seq->startofs = 0;
changed = true;
}
- else if (seq->start <= seq->startdisp) {
+ else {
seq->startstill = 0;
seq->startofs = seq->startdisp - seq->start;
changed = true;
@@ -1411,28 +1447,25 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
}
if (changed) { /* Got new strips? */
- Sequence *seq;
if (ignore_selection) {
if (use_cursor_position) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->enddisp == split_frame && seq->machine == split_channel) {
seq_selected = seq->flag & SEQ_ALLSEL;
}
}
- SEQ_CURRENT_END;
if (!seq_selected) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->startdisp == split_frame && seq->machine == split_channel) {
seq->flag &= ~SEQ_ALLSEL;
}
}
- SEQ_CURRENT_END;
}
}
}
else {
if (split_side != SEQ_SIDE_BOTH) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (split_side == SEQ_SIDE_LEFT) {
if (seq->startdisp >= split_frame) {
seq->flag &= ~SEQ_ALLSEL;
@@ -1444,11 +1477,10 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
}
}
}
- SEQ_CURRENT_END;
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
}
if (changed) {
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1496,19 +1528,16 @@ static void sequencer_split_ui(bContext *UNUSED(C), wmOperator *op)
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- PointerRNA ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
uiLayout *row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "frame", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "side", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "frame", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "side", 0, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "use_cursor_position", 0, NULL, ICON_NONE);
- if (RNA_boolean_get(&ptr, "use_cursor_position")) {
- uiItemR(layout, &ptr, "channel", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_cursor_position", 0, NULL, ICON_NONE);
+ if (RNA_boolean_get(op->ptr, "use_cursor_position")) {
+ uiItemR(layout, op->ptr, "channel", 0, NULL, ICON_NONE);
}
}
@@ -1585,38 +1614,35 @@ void SEQUENCER_OT_split(struct wmOperatorType *ot)
/** \name Duplicate Strips Operator
* \{ */
-static int apply_unique_name_fn(Sequence *seq, void *arg_pt)
-{
- Scene *scene = (Scene *)arg_pt;
- char name[sizeof(seq->name) - 2];
-
- BLI_strncpy_utf8(name, seq->name + 2, sizeof(name));
- SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
- SEQ_dupe_animdata(scene, name, seq->name + 2);
- return 1;
-}
-
static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- ListBase nseqbase = {NULL, NULL};
-
if (ed == NULL) {
return OPERATOR_CANCELLED;
}
- SEQ_sequence_base_dupli_recursive(scene, scene, &nseqbase, ed->seqbasep, SEQ_DUPE_CONTEXT, 0);
+ Sequence *active_seq = SEQ_select_active_get(scene);
+ ListBase duplicated = {NULL, NULL};
- if (nseqbase.first) {
- Sequence *seq = nseqbase.first;
+ SEQ_sequence_base_dupli_recursive(scene, scene, &duplicated, ed->seqbasep, 0, 0);
+ ED_sequencer_deselect_all(scene);
+
+ if (duplicated.first) {
+ Sequence *seq = duplicated.first;
/* Rely on the nseqbase list being added at the end.
* Their UUIDs has been re-generated by the SEQ_sequence_base_dupli_recursive(), */
- BLI_movelisttolist(ed->seqbasep, &nseqbase);
+ BLI_movelisttolist(ed->seqbasep, &duplicated);
+ /* Handle duplicated strips: set active, select, ensure unique name and duplicate animation
+ * data. */
for (; seq; seq = seq->next) {
- SEQ_iterator_recursive_apply(seq, apply_unique_name_fn, scene);
+ if (active_seq != NULL && STREQ(seq->name, active_seq->name)) {
+ SEQ_select_active_set(scene, seq);
+ }
+ seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK);
+ SEQ_ensure_unique_name(seq, scene);
}
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1652,16 +1678,14 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op))
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq;
SEQ_prefetch_stop(scene);
- SEQ_CURRENT_BEGIN (scene->ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->flag & SELECT) {
SEQ_edit_flag_for_removal(scene, ed->seqbasep, seq);
}
}
- SEQ_CURRENT_END;
SEQ_edit_remove_flagged_sequences(scene, ed->seqbasep);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -1789,8 +1813,8 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
/* if (seq->ipo) id_us_min(&seq->ipo->id); */
/* XXX, remove fcurve and assign to split image strips */
- start_ofs = timeline_frame = SEQ_transform_get_left_handle_frame(seq, false);
- frame_end = SEQ_transform_get_right_handle_frame(seq, false);
+ start_ofs = timeline_frame = SEQ_transform_get_left_handle_frame(seq);
+ frame_end = SEQ_transform_get_right_handle_frame(seq);
while (timeline_frame < frame_end) {
/* New seq. */
@@ -1839,7 +1863,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1877,13 +1901,13 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op))
Sequence *active_seq = SEQ_select_active_get(scene);
if (active_seq && active_seq->type == SEQ_TYPE_META && active_seq->flag & SELECT) {
- /* Enter metastrip. */
+ /* Enter meta-strip. */
SEQ_meta_stack_alloc(ed, active_seq);
SEQ_seqbase_active_set(ed, &active_seq->seqbase);
SEQ_select_active_set(scene, NULL);
}
else {
- /* Exit metastrip if possible. */
+ /* Exit meta-strip if possible. */
if (BLI_listbase_is_empty(&ed->metastack)) {
return OPERATOR_CANCELLED;
}
@@ -1905,7 +1929,7 @@ void SEQUENCER_OT_meta_toggle(wmOperatorType *ot)
/* Identifiers. */
ot->name = "Toggle Meta Strip";
ot->idname = "SEQUENCER_OT_meta_toggle";
- ot->description = "Toggle a metastrip (to edit enclosed strips)";
+ ot->description = "Toggle a meta-strip (to edit enclosed strips)";
/* Api callbacks. */
ot->exec = sequencer_meta_toggle_exec;
@@ -1973,7 +1997,7 @@ void SEQUENCER_OT_meta_make(wmOperatorType *ot)
/* Identifiers. */
ot->name = "Make Meta Strip";
ot->idname = "SEQUENCER_OT_meta_make";
- ot->description = "Group selected strips into a metastrip";
+ ot->description = "Group selected strips into a meta-strip";
/* Api callbacks. */
ot->exec = sequencer_meta_make_exec;
@@ -2024,7 +2048,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- SEQ_sort(scene);
+ SEQ_sort(active_seqbase);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -2036,7 +2060,7 @@ void SEQUENCER_OT_meta_separate(wmOperatorType *ot)
/* Identifiers. */
ot->name = "UnMeta Strip";
ot->idname = "SEQUENCER_OT_meta_separate";
- ot->description = "Put the contents of a metastrip back in the sequencer";
+ ot->description = "Put the contents of a meta-strip back in the sequencer";
/* Api callbacks. */
ot->exec = sequencer_meta_separate_exec;
@@ -2247,7 +2271,7 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -2284,43 +2308,45 @@ void SEQUENCER_OT_swap(wmOperatorType *ot)
static int sequencer_rendersize_exec(bContext *C, wmOperator *UNUSED(op))
{
- int retval = OPERATOR_CANCELLED;
Scene *scene = CTX_data_scene(C);
Sequence *active_seq = SEQ_select_active_get(scene);
StripElem *se = NULL;
- if (active_seq == NULL) {
+ if (active_seq == NULL || active_seq->strip == NULL) {
return OPERATOR_CANCELLED;
}
- if (active_seq->strip) {
- switch (active_seq->type) {
- case SEQ_TYPE_IMAGE:
- se = SEQ_render_give_stripelem(active_seq, scene->r.cfra);
- break;
- case SEQ_TYPE_MOVIE:
- se = active_seq->strip->stripdata;
- break;
- case SEQ_TYPE_SCENE:
- case SEQ_TYPE_META:
- case SEQ_TYPE_SOUND_RAM:
- case SEQ_TYPE_SOUND_HD:
- default:
- break;
- }
+ switch (active_seq->type) {
+ case SEQ_TYPE_IMAGE:
+ se = SEQ_render_give_stripelem(active_seq, scene->r.cfra);
+ break;
+ case SEQ_TYPE_MOVIE:
+ se = active_seq->strip->stripdata;
+ break;
+ default:
+ return OPERATOR_CANCELLED;
}
- if (se) {
- /* Prevent setting the render size if sequence values aren't initialized. */
- if ((se->orig_width > 0) && (se->orig_height > 0)) {
- scene->r.xsch = se->orig_width;
- scene->r.ysch = se->orig_height;
- WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
- retval = OPERATOR_FINISHED;
- }
+ if (se == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Prevent setting the render size if sequence values aren't initialized. */
+ if (se->orig_width <= 0 || se->orig_height <= 0) {
+ return OPERATOR_CANCELLED;
}
- return retval;
+ scene->r.xsch = se->orig_width;
+ scene->r.ysch = se->orig_height;
+
+ active_seq->strip->transform->scale_x = active_seq->strip->transform->scale_y = 1.0f;
+ active_seq->strip->transform->xofs = active_seq->strip->transform->yofs = 0.0f;
+
+ SEQ_relations_invalidate_cache_preprocessed(scene, active_seq);
+ WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ return OPERATOR_FINISHED;
}
void SEQUENCER_OT_rendersize(wmOperatorType *ot)
@@ -2418,17 +2444,15 @@ void SEQUENCER_OT_copy(wmOperatorType *ot)
void ED_sequencer_deselect_all(Scene *scene)
{
- Sequence *seq;
Editing *ed = SEQ_editing_get(scene, false);
if (ed == NULL) {
return;
}
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
seq->flag &= ~SEQ_ALLSEL;
}
- SEQ_CURRENT_END;
}
static int sequencer_paste_exec(bContext *C, wmOperator *op)
@@ -2475,7 +2499,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op)
for (iseq = iseq_first; iseq; iseq = iseq->next) {
/* Make sure, that pasted strips have unique names. */
- SEQ_iterator_recursive_apply(iseq, apply_unique_name_fn, scene);
+ SEQ_ensure_unique_name(iseq, scene);
/* Translate after name has been changed, otherwise this will affect animdata of original
* strip. */
SEQ_transform_translate_sequence(scene, iseq, ofs);
@@ -3074,7 +3098,7 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op)
scene->r.efra = efra;
}
- WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_FRAME_RANGE, scene);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c
index f11a879912c..9b3ecacceb9 100644
--- a/source/blender/editors/space_sequencer/sequencer_modifier.c
+++ b/source/blender/editors/space_sequencer/sequencer_modifier.c
@@ -230,14 +230,13 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Editing *ed = scene->ed;
Sequence *seq = SEQ_select_active_get(scene);
- Sequence *seq_iter;
const int type = RNA_enum_get(op->ptr, "type");
if (!seq || !seq->modifiers.first) {
return OPERATOR_CANCELLED;
}
- SEQ_CURRENT_BEGIN (ed, seq_iter) {
+ LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) {
if (seq_iter->flag & SELECT) {
if (seq_iter == seq) {
continue;
@@ -259,7 +258,6 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op)
SEQ_modifier_list_copy(seq_iter, seq);
}
}
- SEQ_CURRENT_END;
SEQ_relations_invalidate_cache_preprocessed(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
diff --git a/source/blender/editors/space_sequencer/sequencer_proxy.c b/source/blender/editors/space_sequencer/sequencer_proxy.c
index e44afde371a..2dcc2d389d9 100644
--- a/source/blender/editors/space_sequencer/sequencer_proxy.c
+++ b/source/blender/editors/space_sequencer/sequencer_proxy.c
@@ -58,7 +58,6 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
ScrArea *area = CTX_wm_area(C);
- Sequence *seq;
if (ed == NULL) {
return;
@@ -70,7 +69,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
GSet *file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list");
bool selected = false; /* Check for no selected strips */
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE) || (seq->flag & SELECT) == 0) {
continue;
}
@@ -92,7 +91,6 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping", seq->name);
}
}
- SEQ_CURRENT_END;
BLI_gset_free(file_list, MEM_freeN);
@@ -101,7 +99,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
return;
}
- if (selected && !WM_jobs_is_running(wm_job)) {
+ if (!WM_jobs_is_running(wm_job)) {
G.is_break = false;
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
@@ -124,7 +122,6 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq;
GSet *file_list;
if (ed == NULL) {
@@ -133,7 +130,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list");
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if ((seq->flag & SELECT)) {
ListBase queue = {NULL, NULL};
LinkData *link;
@@ -150,7 +147,6 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
SEQ_relations_free_imbuf(scene, &ed->seqbase, false);
}
}
- SEQ_CURRENT_END;
BLI_gset_free(file_list, MEM_freeN);
@@ -189,7 +185,6 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq;
bool proxy_25 = RNA_boolean_get(op->ptr, "proxy_25");
bool proxy_50 = RNA_boolean_get(op->ptr, "proxy_50");
bool proxy_75 = RNA_boolean_get(op->ptr, "proxy_75");
@@ -201,7 +196,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
turnon = false;
}
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if ((seq->flag & SELECT)) {
if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) {
SEQ_proxy_set(seq, turnon);
@@ -246,7 +241,6 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
}
}
}
- SEQ_CURRENT_END;
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index 5f0a18fbd0b..7e515271b13 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -26,7 +26,6 @@
#include <string.h>
#include "BLI_blenlib.h"
-#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -43,6 +42,7 @@
#include "SEQ_iterator.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "SEQ_transform.h"
/* For menu, popup, icons, etc. */
@@ -548,14 +548,14 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
const float x = UI_view2d_region_to_view_x(v2d, mval[0]);
- SEQ_CURRENT_BEGIN (ed, seq) {
- if (((x < CFRA) && (seq->enddisp <= CFRA)) || ((x >= CFRA) && (seq->startdisp >= CFRA))) {
+ LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) {
+ if (((x < CFRA) && (seq_iter->enddisp <= CFRA)) ||
+ ((x >= CFRA) && (seq_iter->startdisp >= CFRA))) {
/* Select left or right. */
- seq->flag |= SELECT;
- recurs_sel_seq(seq);
+ seq_iter->flag |= SELECT;
+ recurs_sel_seq(seq_iter);
}
}
- SEQ_CURRENT_END;
{
SpaceSeq *sseq = CTX_wm_space_seq(C);
@@ -808,7 +808,7 @@ static bool select_linked_internal(Scene *scene)
bool changed = false;
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
- if ((seq->flag & SELECT) != 0) {
+ if ((seq->flag & SELECT) == 0) {
continue;
}
/* Only get unselected neighbors. */
@@ -1170,7 +1170,6 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
Editing *ed = SEQ_editing_get(scene, false);
const bool extend = RNA_boolean_get(op->ptr, "extend");
const int side = RNA_enum_get(op->ptr, "side");
- Sequence *seq;
if (ed == NULL) {
return OPERATOR_CANCELLED;
@@ -1179,7 +1178,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
ED_sequencer_deselect_all(scene);
}
const int timeline_frame = CFRA;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
bool test = false;
switch (side) {
case -1:
@@ -1188,8 +1187,8 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
case 1:
test = (timeline_frame <= seq->startdisp);
break;
- case 0:
- test = (timeline_frame <= seq->enddisp) && (timeline_frame >= seq->startdisp);
+ case 2:
+ test = SEQ_time_strip_intersects_frame(seq, timeline_frame);
break;
}
@@ -1198,7 +1197,6 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
recurs_sel_seq(seq);
}
}
- SEQ_CURRENT_END;
ED_outliner_select_sync_from_sequence_tag(C);
@@ -1212,6 +1210,7 @@ void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot)
static const EnumPropertyItem sequencer_select_left_right_types[] = {
{-1, "LEFT", 0, "Left", "Select to the left of the current frame"},
{1, "RIGHT", 0, "Right", "Select to the right of the current frame"},
+ {2, "CURRENT", 0, "Current Frame", "Select intersecting with the current frame"},
{0, NULL, 0, NULL, NULL},
};
@@ -1483,58 +1482,51 @@ static const EnumPropertyItem sequencer_prop_select_grouped_types[] = {
static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
const bool is_sound = SEQ_IS_SOUND(actseq);
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
const bool is_effect = SEQ_IS_EFFECT(actseq);
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) &&
(is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
const char *dir = actseq->strip ? actseq->strip->dir : NULL;
@@ -1543,45 +1535,41 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
}
if (SEQ_HAS_PATH(actseq) && dir) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip &&
STREQ(seq->strip->dir, dir)) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_SCENE) {
Scene *sce = actseq->scene;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_MOVIECLIP) {
MovieClip *clip = actseq->clip;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP &&
seq->clip == clip) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_MASK) {
struct Mask *mask = actseq->mask;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
}
return changed;
@@ -1589,7 +1577,6 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
bool effects[SEQ_TYPE_MAX + 1];
@@ -1597,15 +1584,14 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann
effects[i] = false;
}
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) &&
ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) {
effects[seq->type] = true;
}
}
- SEQ_CURRENT_END;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) {
if (seq->seq1) {
seq->seq1->flag |= SELECT;
@@ -1619,86 +1605,65 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq)
{
- Sequence *seq;
bool changed = false;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
-static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int channel)
+/* Query strips that are in lower channel and intersect in time with seq_reference. */
+static void query_lower_channel_strips(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection)
{
- Sequence *seq = NULL;
- bool changed = false;
- const bool is_audio = ((actseq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(actseq));
- int startdisp = actseq->startdisp;
- int enddisp = actseq->enddisp;
- int machine = actseq->machine;
- SeqIterator iter;
-
- SEQ_CURRENT_BEGIN (ed, seq) {
- seq->tmp = NULL;
- }
- SEQ_CURRENT_END;
-
- actseq->tmp = POINTER_FROM_INT(true);
-
- for (SEQ_iterator_begin(ed, &iter, true); iter.valid; SEQ_iterator_next(&iter)) {
- seq = iter.seq;
-
- /* Ignore all seqs already selected. */
- /* Ignore all seqs not sharing some time with active one. */
- /* Ignore all seqs of incompatible types (audio vs video). */
- if (!SEQ_CHANNEL_CHECK(seq, channel) || (seq->flag & SELECT) || (seq->startdisp >= enddisp) ||
- (seq->enddisp < startdisp) || (!is_audio && SEQ_IS_SOUND(seq)) ||
- (is_audio && !((seq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(seq)))) {
- continue;
+ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
+ if (seq_test->machine > seq_reference->machine) {
+ continue; /* Not lower channel. */
}
+ if (seq_test->enddisp <= seq_reference->startdisp ||
+ seq_test->startdisp >= seq_reference->enddisp) {
+ continue; /* Not intersecting in time. */
+ }
+ SEQ_collection_append_strip(seq_test, collection);
+ }
+}
- /* If the seq is an effect one, we need extra checking. */
- if (SEQ_IS_EFFECT(seq) && ((seq->seq1 && seq->seq1->tmp) || (seq->seq2 && seq->seq2->tmp) ||
- (seq->seq3 && seq->seq3->tmp))) {
- if (startdisp > seq->startdisp) {
- startdisp = seq->startdisp;
- }
- if (enddisp < seq->enddisp) {
- enddisp = seq->enddisp;
- }
- if (machine < seq->machine) {
- machine = seq->machine;
- }
-
- seq->tmp = POINTER_FROM_INT(true);
+/* Select all strips within time range and with lower channel of initial selection. Then select
+ * effect chains of these strips. */
+static bool select_grouped_effect_link(Editing *ed,
+ Sequence *UNUSED(actseq),
+ const int UNUSED(channel))
+{
+ ListBase *seqbase = SEQ_active_seqbase_get(ed);
- seq->flag |= SELECT;
- changed = true;
+ /* Get collection of strips. */
+ SeqCollection *collection = SEQ_query_selected_strips(seqbase);
+ const int selected_strip_count = BLI_gset_len(collection->set);
+ SEQ_collection_expand(seqbase, collection, query_lower_channel_strips);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
- /* Unfortunately, we must restart checks from the beginning. */
- SEQ_iterator_end(&iter);
- SEQ_iterator_begin(ed, &iter, true);
- }
+ /* Check if other strips will be affected. */
+ const bool changed = BLI_gset_len(collection->set) > selected_strip_count;
- /* Video strips below active one, or any strip for audio (order doesn't matter here). */
- else if (seq->machine < machine || is_audio) {
- seq->flag |= SELECT;
- changed = true;
- }
+ /* Actual logic. */
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ seq->flag |= SELECT;
}
- SEQ_iterator_end(&iter);
+
+ SEQ_collection_free(collection);
return changed;
}
@@ -1711,7 +1676,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq, *actseq = SEQ_select_active_get(scene);
+ Sequence *actseq = SEQ_select_active_get(scene);
if (actseq == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active sequence!");
@@ -1725,11 +1690,10 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
bool changed = false;
if (!extend) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
seq->flag &= ~SELECT;
changed = true;
}
- SEQ_CURRENT_END;
}
switch (type) {
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index a77e74ffd93..b2a0905d4a2 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -17,9 +17,9 @@
set(INC
../include
+ ../../blenfont
../../blenkernel
../../blenlib
- ../../blenfont
../../bmesh
../../depsgraph
../../functions
@@ -33,15 +33,23 @@ set(INC
set(SRC
space_spreadsheet.cc
- spreadsheet_column_layout.cc
+ spreadsheet_context.cc
+ spreadsheet_column.cc
+ spreadsheet_data_source.cc
+ spreadsheet_data_source_geometry.cc
spreadsheet_draw.cc
- spreadsheet_from_geometry.cc
+ spreadsheet_layout.cc
spreadsheet_ops.cc
+ spreadsheet_context.hh
+ spreadsheet_cell_value.hh
+ spreadsheet_column.hh
+ spreadsheet_column_values.hh
+ spreadsheet_data_source.hh
+ spreadsheet_data_source_geometry.hh
spreadsheet_draw.hh
- spreadsheet_column_layout.hh
- spreadsheet_from_geometry.hh
spreadsheet_intern.hh
+ spreadsheet_layout.hh
)
set(LIB
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index 0f7709b464e..1f0b5d5d13e 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -17,12 +17,12 @@
#include <cstring>
#include "BLI_listbase.h"
-#include "BLI_resource_collector.hh"
#include "BKE_screen.h"
#include "ED_screen.h"
#include "ED_space_api.h"
+#include "ED_spreadsheet.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -41,12 +41,16 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "BLF_api.h"
+
#include "spreadsheet_intern.hh"
-#include "spreadsheet_column_layout.hh"
-#include "spreadsheet_from_geometry.hh"
+#include "spreadsheet_context.hh"
+#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
+#include "spreadsheet_layout.hh"
+using namespace blender;
using namespace blender::ed::spreadsheet;
static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
@@ -85,6 +89,13 @@ static void spreadsheet_free(SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
MEM_SAFE_FREE(sspreadsheet->runtime);
+
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ spreadsheet_column_free(column);
+ }
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ spreadsheet_context_free(context);
+ }
}
static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area)
@@ -102,6 +113,18 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old);
sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime);
+ BLI_listbase_clear(&sspreadsheet_new->columns);
+ LISTBASE_FOREACH (SpreadsheetColumn *, src_column, &sspreadsheet_old->columns) {
+ SpreadsheetColumn *new_column = spreadsheet_column_copy(src_column);
+ BLI_addtail(&sspreadsheet_new->columns, new_column);
+ }
+
+ BLI_listbase_clear(&sspreadsheet_new->context_path);
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, src_context, &sspreadsheet_old->context_path) {
+ SpreadsheetContext *new_context = spreadsheet_context_copy(src_context);
+ BLI_addtail(&sspreadsheet_new->context_path, new_context);
+ }
+
return (SpaceLink *)sspreadsheet_new;
}
@@ -109,6 +132,24 @@ static void spreadsheet_keymap(wmKeyConfig *UNUSED(keyconf))
{
}
+static void spreadsheet_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+{
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink;
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ if ((ID *)object_context->object == old_id) {
+ if (new_id && GS(new_id->name) == ID_OB) {
+ object_context->object = (Object *)new_id;
+ }
+ else {
+ object_context->object = nullptr;
+ }
+ }
+ }
+ }
+}
+
static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
{
region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM;
@@ -123,57 +164,212 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler(&region->handlers, keymap);
}
-static ID *get_used_id(const bContext *C)
+ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet)
+{
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ return nullptr;
+ }
+ SpreadsheetContext *root_context = (SpreadsheetContext *)sspreadsheet->context_path.first;
+ if (root_context->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return nullptr;
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)root_context;
+ return (ID *)object_context->object;
+}
+
+/* Check if the pinned context still exists. If it doesn't try to find a new context. */
+static void update_pinned_context_path_if_outdated(const bContext *C)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (sspreadsheet->pinned_id != nullptr) {
- return sspreadsheet->pinned_id;
+
+ /* Currently, this only checks if the object has been deleted. In the future we can have a more
+ * sophisticated check for the entire context (including modifier and nodes). */
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ if (object_context->object == nullptr) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ break;
+ }
+ }
+ }
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ Object *active_object = CTX_data_active_object(C);
+ if (active_object != nullptr) {
+ SpreadsheetContext *new_context = spreadsheet_context_new(SPREADSHEET_CONTEXT_OBJECT);
+ ((SpreadsheetContextObject *)new_context)->object = active_object;
+ BLI_addtail(&sspreadsheet->context_path, new_context);
+ }
+ }
+
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ /* Don't pin empty context_path, that could be annoying. */
+ sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED;
}
+}
+
+static void update_context_path_from_context(const bContext *C)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
Object *active_object = CTX_data_active_object(C);
- return (ID *)active_object;
+ if (active_object == nullptr) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ return;
+ }
+ if (!BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ SpreadsheetContext *root_context = (SpreadsheetContext *)sspreadsheet->context_path.first;
+ if (root_context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)root_context;
+ if (object_context->object != active_object) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ }
+ }
+ }
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ SpreadsheetContext *new_context = spreadsheet_context_new(SPREADSHEET_CONTEXT_OBJECT);
+ ((SpreadsheetContextObject *)new_context)->object = active_object;
+ BLI_addtail(&sspreadsheet->context_path, new_context);
+ }
}
-class FallbackSpreadsheetDrawer : public SpreadsheetDrawer {
-};
+static void update_context_path(const bContext *C)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
+ update_pinned_context_path_if_outdated(C);
+ }
+ else {
+ update_context_path_from_context(C);
+ }
+}
-static void gather_spreadsheet_columns(const bContext *C,
- SpreadsheetColumnLayout &column_layout,
- blender::ResourceCollector &resources)
+static std::unique_ptr<DataSource> get_data_source(const bContext *C)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- ID *used_id = get_used_id(C);
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet);
if (used_id == nullptr) {
- return;
+ return {};
}
const ID_Type id_type = GS(used_id->name);
if (id_type != ID_OB) {
- return;
+ return {};
}
Object *object_orig = (Object *)used_id;
- if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) {
- return;
+ if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
+ return {};
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
if (object_eval == nullptr) {
- return;
+ return {};
+ }
+
+ return data_source_from_geometry(C, object_eval);
+}
+
+static float get_column_width(const ColumnValues &values)
+{
+ if (values.default_width > 0) {
+ return values.default_width;
}
+ const int fontid = UI_style_get()->widget.uifont_id;
+ BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi);
+ const StringRefNull name = values.name();
+ const float name_width = BLF_width(fontid, name.data(), name.size());
+ return std::max<float>(name_width / UI_UNIT_X + 1.0f, 3.0f);
+}
- return spreadsheet_columns_from_geometry(C, object_eval, column_layout, resources);
+static float get_column_width_in_pixels(const ColumnValues &values)
+{
+ return get_column_width(values) * SPREADSHEET_WIDTH_UNIT;
+}
+
+static int get_index_column_width(const int tot_rows)
+{
+ const int fontid = UI_style_get()->widget.uifont_id;
+ BLF_size(fontid, UI_style_get_dpi()->widget.points * U.pixelsize, U.dpi);
+ return std::to_string(std::max(0, tot_rows - 1)).size() * BLF_width(fontid, "0", 1) +
+ UI_UNIT_X * 0.75;
+}
+
+static void update_visible_columns(ListBase &columns, DataSource &data_source)
+{
+ Set<SpreadsheetColumnID> used_ids;
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &columns) {
+ std::unique_ptr<ColumnValues> values = data_source.get_column_values(*column->id);
+ /* Remove columns that don't exist anymore. */
+ if (!values) {
+ BLI_remlink(&columns, column);
+ spreadsheet_column_free(column);
+ continue;
+ }
+
+ if (!used_ids.add(*column->id)) {
+ /* Remove duplicate columns for now. */
+ BLI_remlink(&columns, column);
+ spreadsheet_column_free(column);
+ continue;
+ }
+ }
+
+ data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) {
+ std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
+ if (values) {
+ if (used_ids.add(column_id)) {
+ SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
+ SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
+ BLI_addtail(&columns, new_column);
+ }
+ }
+ });
}
static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ update_context_path(C);
- blender::ResourceCollector resources;
- SpreadsheetColumnLayout column_layout;
- gather_spreadsheet_columns(C, column_layout, resources);
+ std::unique_ptr<DataSource> data_source = get_data_source(C);
+ if (!data_source) {
+ data_source = std::make_unique<DataSource>();
+ }
+
+ update_visible_columns(sspreadsheet->columns, *data_source);
+
+ SpreadsheetLayout spreadsheet_layout;
+ ResourceScope scope;
+
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ std::unique_ptr<ColumnValues> values_ptr = data_source->get_column_values(*column->id);
+ /* Should have been removed before if it does not exist anymore. */
+ BLI_assert(values_ptr);
+ const ColumnValues *values = scope.add(std::move(values_ptr), __func__);
+ const int width = get_column_width_in_pixels(*values);
+ spreadsheet_layout.columns.append({values, width});
+ }
+
+ const int tot_rows = data_source->tot_rows();
+ spreadsheet_layout.index_column_width = get_index_column_width(tot_rows);
+ spreadsheet_layout.row_indices = IndexRange(tot_rows).as_span();
+
+ if (const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>(
+ data_source.get())) {
+ Object *object_eval = geometry_data_source->object_eval();
+ Object *object_orig = DEG_get_original_object(object_eval);
+ if (object_orig->type == OB_MESH) {
+ if (object_orig->mode == OB_MODE_EDIT) {
+ if (sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY) {
+ spreadsheet_layout.row_indices = geometry_data_source->get_selected_element_indices();
+ }
+ }
+ }
+ }
- sspreadsheet->runtime->visible_rows = column_layout.row_indices.size();
- sspreadsheet->runtime->tot_columns = column_layout.columns.size();
- sspreadsheet->runtime->tot_rows = column_layout.tot_rows;
+ sspreadsheet->runtime->tot_columns = spreadsheet_layout.columns.size();
+ sspreadsheet->runtime->tot_rows = tot_rows;
+ sspreadsheet->runtime->visible_rows = spreadsheet_layout.row_indices.size();
- std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_column_layout(column_layout);
+ std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_layout(spreadsheet_layout);
draw_spreadsheet_in_region(C, region, *drawer);
/* Tag footer for redraw, because the main region updates data for the footer. */
@@ -208,6 +404,7 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param
}
break;
}
+ case NC_TEXTURE:
case NC_GEOM: {
ED_region_tag_redraw(region);
break;
@@ -222,6 +419,7 @@ static void spreadsheet_header_region_init(wmWindowManager *UNUSED(wm), ARegion
static void spreadsheet_header_region_draw(const bContext *C, ARegion *region)
{
+ update_context_path(C);
ED_region_header(C, region);
}
@@ -327,6 +525,7 @@ void ED_spacetype_spreadsheet(void)
st->duplicate = spreadsheet_duplicate;
st->operatortypes = spreadsheet_operatortypes;
st->keymap = spreadsheet_keymap;
+ st->id_remap = spreadsheet_id_remap;
/* regions: main window */
art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spacetype spreadsheet region");
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
new file mode 100644
index 00000000000..c9b73aabf96
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "BLI_color.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+
+struct Object;
+struct Collection;
+
+namespace blender::ed::spreadsheet {
+
+struct ObjectCellValue {
+ const Object *object;
+};
+
+struct CollectionCellValue {
+ const Collection *collection;
+};
+
+/**
+ * This is a type that can hold the value of a cell in a spreadsheet. This type allows us to
+ * decouple the drawing of individual cells from the code that generates the data to be displayed.
+ */
+class CellValue {
+ public:
+ /* The implementation just uses a bunch of `std::option` for now. Unfortunately, we cannot use
+ * `std::variant` yet, due to missing compiler support. This type can really be optimized more,
+ * but it does not really matter too much currently. */
+
+ std::optional<int> value_int;
+ std::optional<float> value_float;
+ std::optional<bool> value_bool;
+ std::optional<float2> value_float2;
+ std::optional<float3> value_float3;
+ std::optional<ColorGeometry4f> value_color;
+ std::optional<ObjectCellValue> value_object;
+ std::optional<CollectionCellValue> value_collection;
+};
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
new file mode 100644
index 00000000000..de40545fdae
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
@@ -0,0 +1,72 @@
+/*
+ * 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 "DNA_space_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_hash.hh"
+#include "BLI_string.h"
+#include "BLI_string_ref.hh"
+
+#include "spreadsheet_column.hh"
+
+namespace blender::ed::spreadsheet {
+
+SpreadsheetColumnID *spreadsheet_column_id_new()
+{
+ SpreadsheetColumnID *column_id = (SpreadsheetColumnID *)MEM_callocN(sizeof(SpreadsheetColumnID),
+ __func__);
+ return column_id;
+}
+
+SpreadsheetColumnID *spreadsheet_column_id_copy(const SpreadsheetColumnID *src_column_id)
+{
+ SpreadsheetColumnID *new_column_id = spreadsheet_column_id_new();
+ new_column_id->name = BLI_strdup(src_column_id->name);
+ return new_column_id;
+}
+
+void spreadsheet_column_id_free(SpreadsheetColumnID *column_id)
+{
+ if (column_id->name != nullptr) {
+ MEM_freeN(column_id->name);
+ }
+ MEM_freeN(column_id);
+}
+
+SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id)
+{
+ SpreadsheetColumn *column = (SpreadsheetColumn *)MEM_callocN(sizeof(SpreadsheetColumn),
+ __func__);
+ column->id = column_id;
+ return column;
+}
+
+SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column)
+{
+ SpreadsheetColumnID *new_column_id = spreadsheet_column_id_copy(src_column->id);
+ SpreadsheetColumn *new_column = spreadsheet_column_new(new_column_id);
+ return new_column;
+}
+
+void spreadsheet_column_free(SpreadsheetColumn *column)
+{
+ spreadsheet_column_id_free(column->id);
+ MEM_freeN(column);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
new file mode 100644
index 00000000000..bb245851d55
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
@@ -0,0 +1,48 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "DNA_space_types.h"
+
+#include "BLI_hash.hh"
+
+namespace blender {
+template<> struct DefaultHash<SpreadsheetColumnID> {
+ uint64_t operator()(const SpreadsheetColumnID &column_id) const
+ {
+ return get_default_hash(StringRef(column_id.name));
+ }
+};
+} // namespace blender
+
+inline bool operator==(const SpreadsheetColumnID &a, const SpreadsheetColumnID &b)
+{
+ using blender::StringRef;
+ return StringRef(a.name) == StringRef(b.name);
+}
+
+namespace blender::ed::spreadsheet {
+
+SpreadsheetColumnID *spreadsheet_column_id_new();
+SpreadsheetColumnID *spreadsheet_column_id_copy(const SpreadsheetColumnID *src_column_id);
+void spreadsheet_column_id_free(SpreadsheetColumnID *column_id);
+
+SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id);
+SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column);
+void spreadsheet_column_free(SpreadsheetColumn *column);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc
deleted file mode 100644
index 46760c0dd4e..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc
+++ /dev/null
@@ -1,230 +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.
- */
-
-#include <iomanip>
-#include <sstream>
-
-#include "spreadsheet_column_layout.hh"
-
-#include "DNA_userdef_types.h"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "BLF_api.h"
-
-namespace blender::ed::spreadsheet {
-
-class ColumnLayoutDrawer : public SpreadsheetDrawer {
- private:
- const SpreadsheetColumnLayout &column_layout_;
- Vector<int> column_widths_;
-
- public:
- ColumnLayoutDrawer(const SpreadsheetColumnLayout &column_layout) : column_layout_(column_layout)
- {
- tot_columns = column_layout.columns.size();
- tot_rows = column_layout.row_indices.size();
-
- const int fontid = UI_style_get()->widget.uifont_id;
- /* Use a consistent font size for the width calculation. */
- BLF_size(fontid, 11 * U.pixelsize, U.dpi);
-
- /* The width of the index column depends on the maximum row index. */
- left_column_width = std::to_string(std::max(0, column_layout_.tot_rows - 1)).size() *
- BLF_width(fontid, "0", 1) +
- UI_UNIT_X * 0.75;
-
- /* The column widths depend on the column name widths. */
- const int minimum_column_width = 3 * UI_UNIT_X;
- const int header_name_padding = UI_UNIT_X;
- for (const SpreadsheetColumn *column : column_layout_.columns) {
- if (column->default_width == 0.0f) {
- StringRefNull name = column->name();
- const int name_width = BLF_width(fontid, name.data(), name.size());
- const int width = std::max(name_width + header_name_padding, minimum_column_width);
- column_widths_.append(width);
- }
- else {
- column_widths_.append(column->default_width * UI_UNIT_X);
- }
- }
- }
-
- void draw_top_row_cell(int column_index, const CellDrawParams &params) const final
- {
- const StringRefNull name = column_layout_.columns[column_index]->name();
- uiBut *but = uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_NONE,
- name.c_str(),
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- /* Center-align column headers. */
- UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
- UI_but_drawflag_disable(but, UI_BUT_TEXT_RIGHT);
- }
-
- void draw_left_column_cell(int row_index, const CellDrawParams &params) const final
- {
- const int real_index = column_layout_.row_indices[row_index];
- std::string index_str = std::to_string(real_index);
- uiBut *but = uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_NONE,
- index_str.c_str(),
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- /* Right-align indices. */
- UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
- UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
- }
-
- void draw_content_cell(int row_index, int column_index, const CellDrawParams &params) const final
- {
- const int real_index = column_layout_.row_indices[row_index];
- const SpreadsheetColumn &column = *column_layout_.columns[column_index];
- CellValue cell_value;
- column.get_value(real_index, cell_value);
-
- if (cell_value.value_int.has_value()) {
- const int value = *cell_value.value_int;
- const std::string value_str = std::to_string(value);
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_NONE,
- value_str.c_str(),
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_float.has_value()) {
- const float value = *cell_value.value_float;
- std::stringstream ss;
- ss << std::fixed << std::setprecision(3) << value;
- const std::string value_str = ss.str();
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_NONE,
- value_str.c_str(),
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_bool.has_value()) {
- const bool value = *cell_value.value_bool;
- const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- icon,
- "",
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_object.has_value()) {
- const ObjectCellValue value = *cell_value.value_object;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_OBJECT_DATA,
- reinterpret_cast<const ID *const>(value.object)->name + 2,
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_collection.has_value()) {
- const CollectionCellValue value = *cell_value.value_collection;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_OUTLINER_COLLECTION,
- reinterpret_cast<const ID *const>(value.collection)->name + 2,
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- }
-
- int column_width(int column_index) const final
- {
- return column_widths_[column_index];
- }
-};
-
-std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_column_layout(
- const SpreadsheetColumnLayout &column_layout)
-{
- return std::make_unique<ColumnLayoutDrawer>(column_layout);
-}
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh
deleted file mode 100644
index 611337df007..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-#include <optional>
-
-#include "spreadsheet_draw.hh"
-
-struct Object;
-struct Collection;
-
-namespace blender::ed::spreadsheet {
-
-struct ObjectCellValue {
- const Object *object;
-};
-
-struct CollectionCellValue {
- const Collection *collection;
-};
-
-/**
- * This is a small type that can hold the value of a cell in a spreadsheet. This type allows us to
- * decouple the drawing of individual cells from the code that generates the data to be displayed.
- */
-class CellValue {
- public:
- /* The implementation just uses a bunch of `std::option` for now. Unfortunately, we cannot use
- * `std::variant` yet, due to missing compiler support. This type can really be optimized more,
- * but it does not really matter too much currently. */
-
- std::optional<int> value_int;
- std::optional<float> value_float;
- std::optional<bool> value_bool;
- std::optional<ObjectCellValue> value_object;
- std::optional<CollectionCellValue> value_collection;
-};
-
-/**
- * This represents a column in a spreadsheet. It has a name and provides a value for all the cells
- * in the column.
- */
-class SpreadsheetColumn {
- protected:
- std::string name_;
-
- public:
- SpreadsheetColumn(std::string name) : name_(std::move(name))
- {
- }
-
- virtual ~SpreadsheetColumn() = default;
-
- virtual void get_value(int index, CellValue &r_cell_value) const = 0;
-
- StringRefNull name() const
- {
- return name_;
- }
-
- /* The default width of newly created columns, in UI units. */
- float default_width = 0.0f;
-};
-
-/* Utility class for the function below. */
-template<typename GetValueF> class LambdaSpreadsheetColumn : public SpreadsheetColumn {
- private:
- GetValueF get_value_;
-
- public:
- LambdaSpreadsheetColumn(std::string name, GetValueF get_value)
- : SpreadsheetColumn(std::move(name)), get_value_(std::move(get_value))
- {
- }
-
- void get_value(int index, CellValue &r_cell_value) const final
- {
- get_value_(index, r_cell_value);
- }
-};
-
-/* Utility function that simplifies creating a spreadsheet column from a lambda function. */
-template<typename GetValueF>
-std::unique_ptr<SpreadsheetColumn> spreadsheet_column_from_function(std::string name,
- GetValueF get_value)
-{
- return std::make_unique<LambdaSpreadsheetColumn<GetValueF>>(std::move(name),
- std::move(get_value));
-}
-
-/* This contains information required to create a spreadsheet drawer from columns. */
-struct SpreadsheetColumnLayout {
- Vector<const SpreadsheetColumn *> columns;
- Span<int64_t> row_indices;
- int tot_rows = 0;
-};
-
-std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_column_layout(
- const SpreadsheetColumnLayout &column_layout);
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
new file mode 100644
index 00000000000..373c988a41c
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
@@ -0,0 +1,92 @@
+/*
+ * 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_string_ref.hh"
+
+#include "spreadsheet_cell_value.hh"
+
+namespace blender::ed::spreadsheet {
+
+/**
+ * This represents a column in a spreadsheet. It has a name and provides a value for all the cells
+ * in the column.
+ */
+class ColumnValues {
+ protected:
+ std::string name_;
+ int size_;
+
+ public:
+ ColumnValues(std::string name, const int size) : name_(std::move(name)), size_(size)
+ {
+ }
+
+ virtual ~ColumnValues() = default;
+
+ virtual void get_value(int index, CellValue &r_cell_value) const = 0;
+
+ StringRefNull name() const
+ {
+ return name_;
+ }
+
+ int size() const
+ {
+ return size_;
+ }
+
+ /* The default width of newly created columns, in UI units. */
+ float default_width = 0.0f;
+};
+
+/* Utility class for the function below. */
+template<typename GetValueF> class LambdaColumnValues : public ColumnValues {
+ private:
+ GetValueF get_value_;
+
+ public:
+ LambdaColumnValues(std::string name, int size, GetValueF get_value)
+ : ColumnValues(std::move(name), size), get_value_(std::move(get_value))
+ {
+ }
+
+ void get_value(int index, CellValue &r_cell_value) const final
+ {
+ get_value_(index, r_cell_value);
+ }
+};
+
+/* Utility function that simplifies creating a spreadsheet column from a lambda function. */
+template<typename GetValueF>
+std::unique_ptr<ColumnValues> column_values_from_function(std::string name,
+ const int size,
+ GetValueF get_value,
+ const float default_width = 0.0f)
+{
+ std::unique_ptr<ColumnValues> column_values = std::make_unique<LambdaColumnValues<GetValueF>>(
+ std::move(name), size, std::move(get_value));
+ column_values->default_width = default_width;
+ return column_values;
+}
+
+static constexpr float default_float_column_width = 3;
+static constexpr float default_float2_column_width = 2 * default_float_column_width;
+static constexpr float default_float3_column_width = 3 * default_float_column_width;
+static constexpr float default_color_column_width = 4 * default_float_column_width;
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
new file mode 100644
index 00000000000..3eb43338908
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
@@ -0,0 +1,306 @@
+/*
+ * 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 "MEM_guardedalloc.h"
+
+#include "BLI_hash.h"
+#include "BLI_hash.hh"
+#include "BLI_hash_mm2a.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
+
+#include "ED_spreadsheet.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "spreadsheet_context.hh"
+
+namespace blender::ed::spreadsheet {
+
+static SpreadsheetContextObject *spreadsheet_context_object_new()
+{
+ SpreadsheetContextObject *context = (SpreadsheetContextObject *)MEM_callocN(
+ sizeof(SpreadsheetContextObject), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_OBJECT;
+ return context;
+}
+
+static SpreadsheetContextObject *spreadsheet_context_object_copy(
+ const SpreadsheetContextObject *src_context)
+{
+ SpreadsheetContextObject *new_context = spreadsheet_context_object_new();
+ new_context->object = src_context->object;
+ return new_context;
+}
+
+static void spreadsheet_context_object_hash(const SpreadsheetContextObject *context,
+ BLI_HashMurmur2A *mm2)
+{
+ BLI_hash_mm2a_add(mm2, (const uchar *)&context->object, sizeof(Object *));
+}
+
+static void spreadsheet_context_object_free(SpreadsheetContextObject *context)
+{
+ MEM_freeN(context);
+}
+
+static SpreadsheetContextModifier *spreadsheet_context_modifier_new()
+{
+ SpreadsheetContextModifier *context = (SpreadsheetContextModifier *)MEM_callocN(
+ sizeof(SpreadsheetContextModifier), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_MODIFIER;
+ return context;
+}
+
+static SpreadsheetContextModifier *spreadsheet_context_modifier_copy(
+ const SpreadsheetContextModifier *src_context)
+{
+ SpreadsheetContextModifier *new_context = spreadsheet_context_modifier_new();
+ if (src_context->modifier_name) {
+ new_context->modifier_name = BLI_strdup(src_context->modifier_name);
+ }
+ return new_context;
+}
+
+static void spreadsheet_context_modifier_hash(const SpreadsheetContextModifier *context,
+ BLI_HashMurmur2A *mm2)
+{
+ if (context->modifier_name) {
+ BLI_hash_mm2a_add(mm2, (const uchar *)context->modifier_name, strlen(context->modifier_name));
+ }
+}
+
+static void spreadsheet_context_modifier_free(SpreadsheetContextModifier *context)
+{
+ if (context->modifier_name) {
+ MEM_freeN(context->modifier_name);
+ }
+ MEM_freeN(context);
+}
+
+static SpreadsheetContextNode *spreadsheet_context_node_new()
+{
+ SpreadsheetContextNode *context = (SpreadsheetContextNode *)MEM_callocN(
+ sizeof(SpreadsheetContextNode), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_NODE;
+ return context;
+}
+
+static SpreadsheetContextNode *spreadsheet_context_node_copy(
+ const SpreadsheetContextNode *src_context)
+{
+ SpreadsheetContextNode *new_context = spreadsheet_context_node_new();
+ if (src_context->node_name) {
+ new_context->node_name = BLI_strdup(src_context->node_name);
+ }
+ return new_context;
+}
+
+static void spreadsheet_context_node_hash(const SpreadsheetContextNode *context,
+ BLI_HashMurmur2A *mm2)
+{
+ if (context->node_name) {
+ BLI_hash_mm2a_add(mm2, (const uchar *)context->node_name, strlen(context->node_name));
+ }
+}
+
+static void spreadsheet_context_node_free(SpreadsheetContextNode *context)
+{
+ if (context->node_name) {
+ MEM_freeN(context->node_name);
+ }
+ MEM_freeN(context);
+}
+
+SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type)
+{
+ switch (type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return (SpreadsheetContext *)spreadsheet_context_object_new();
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return (SpreadsheetContext *)spreadsheet_context_modifier_new();
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return (SpreadsheetContext *)spreadsheet_context_node_new();
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context)
+{
+ switch (old_context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return (SpreadsheetContext *)spreadsheet_context_object_copy(
+ (const SpreadsheetContextObject *)old_context);
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return (SpreadsheetContext *)spreadsheet_context_modifier_copy(
+ (const SpreadsheetContextModifier *)old_context);
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return (SpreadsheetContext *)spreadsheet_context_node_copy(
+ (const SpreadsheetContextNode *)old_context);
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+static void spreadsheet_context_hash(const SpreadsheetContext *context, BLI_HashMurmur2A *mm2)
+{
+ BLI_hash_mm2a_add_int(mm2, context->type);
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ spreadsheet_context_object_hash((const SpreadsheetContextObject *)context, mm2);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ spreadsheet_context_modifier_hash((const SpreadsheetContextModifier *)context, mm2);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ spreadsheet_context_node_hash((const SpreadsheetContextNode *)context, mm2);
+ break;
+ }
+ }
+}
+
+void spreadsheet_context_free(SpreadsheetContext *context)
+{
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return spreadsheet_context_object_free((SpreadsheetContextObject *)context);
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return spreadsheet_context_modifier_free((SpreadsheetContextModifier *)context);
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return spreadsheet_context_node_free((SpreadsheetContextNode *)context);
+ }
+ }
+ BLI_assert_unreachable();
+}
+
+/**
+ * Tag any data relevant to the spreadsheet's context for recalculation in order to collect
+ * information to display in the editor, which may be cached during evaluation.
+ */
+static void spreadsheet_context_update_tag(SpaceSpreadsheet *sspreadsheet)
+{
+ using namespace blender;
+ Vector<const SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ if (context_path.is_empty()) {
+ return;
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return;
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
+ Object *object = object_context->object;
+ if (object == nullptr) {
+ return;
+ }
+ if (context_path.size() == 1) {
+ /* No need to reevaluate, when the final or original object is viewed. */
+ return;
+ }
+
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+}
+
+} // namespace blender::ed::spreadsheet
+
+SpreadsheetContext *ED_spreadsheet_context_new(int type)
+{
+ return blender::ed::spreadsheet::spreadsheet_context_new((eSpaceSpreadsheet_ContextType)type);
+}
+
+void ED_spreadsheet_context_free(struct SpreadsheetContext *context)
+{
+ blender::ed::spreadsheet::spreadsheet_context_free(context);
+}
+
+void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet)
+{
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ ED_spreadsheet_context_free(context);
+ }
+ BLI_listbase_clear(&sspreadsheet->context_path);
+}
+
+void ED_spreadsheet_context_path_update_tag(SpaceSpreadsheet *sspreadsheet)
+{
+ blender::ed::spreadsheet::spreadsheet_context_update_tag(sspreadsheet);
+}
+
+uint64_t ED_spreadsheet_context_path_hash(SpaceSpreadsheet *sspreadsheet)
+{
+ BLI_HashMurmur2A mm2;
+ BLI_hash_mm2a_init(&mm2, 1234);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ blender::ed::spreadsheet::spreadsheet_context_hash(context, &mm2);
+ }
+ return BLI_hash_mm2a_end(&mm2);
+}
+
+void ED_spreadsheet_set_geometry_node_context(struct SpaceSpreadsheet *sspreadsheet,
+ struct SpaceNode *snode,
+ struct bNode *node)
+{
+ using namespace blender::ed::spreadsheet;
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+
+ Object *object = (Object *)snode->id;
+ ModifierData *modifier = BKE_object_active_modifier(object);
+
+ {
+ SpreadsheetContextObject *context = spreadsheet_context_object_new();
+ context->object = object;
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ {
+ SpreadsheetContextModifier *context = spreadsheet_context_modifier_new();
+ context->modifier_name = BLI_strdup(modifier->name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ {
+ int i;
+ LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
+ if (i == 0) {
+ continue;
+ }
+ SpreadsheetContextNode *context = spreadsheet_context_node_new();
+ context->node_name = BLI_strdup(path->node_name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ }
+ {
+ SpreadsheetContextNode *context = spreadsheet_context_node_new();
+ context->node_name = BLI_strdup(node->name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+
+ sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
+}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_context.hh
index cef731517b9..d71769e42b3 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.hh
@@ -16,19 +16,12 @@
#pragma once
-#include "BKE_geometry_set.hh"
-
-#include "BLI_resource_collector.hh"
-
-#include "spreadsheet_column_layout.hh"
-
-struct bContext;
+#include "DNA_space_types.h"
namespace blender::ed::spreadsheet {
-void spreadsheet_columns_from_geometry(const bContext *C,
- Object *object_eval,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources);
+SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type);
+SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context);
+void spreadsheet_context_free(SpreadsheetContext *context);
} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc
new file mode 100644
index 00000000000..09b8c6b1b54
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc
@@ -0,0 +1,24 @@
+/*
+ * 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 "spreadsheet_data_source.hh"
+
+namespace blender::ed::spreadsheet {
+
+/* Provide a "key function" for the linker. */
+DataSource::~DataSource() = default;
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
new file mode 100644
index 00000000000..de47109a144
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
@@ -0,0 +1,65 @@
+/*
+ * 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_function_ref.hh"
+
+#include "spreadsheet_column.hh"
+#include "spreadsheet_column_values.hh"
+
+namespace blender::ed::spreadsheet {
+
+/**
+ * This class is subclassed to implement different data sources for the spreadsheet. A data source
+ * provides the information that should be displayed. It is not concerned with how data is laid
+ * out in the spreadsheet editor exactly.
+ */
+class DataSource {
+ public:
+ virtual ~DataSource();
+
+ /**
+ * Calls the callback with all the column ids that should be displayed as long as the user does
+ * not manually add or remove columns. The column id can be stack allocated. Therefore, the
+ * callback should not keep a reference to it (and copy it instead).
+ */
+ virtual void foreach_default_column_ids(FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+ {
+ UNUSED_VARS(fn);
+ }
+
+ /**
+ * Returns the column values the given column id. If no data exists for this id, null is
+ * returned.
+ */
+ virtual std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const
+ {
+ UNUSED_VARS(column_id);
+ return {};
+ }
+
+ /**
+ * Returns the number of rows in columns returned by #get_column_values.
+ */
+ virtual int tot_rows() const
+ {
+ return 0;
+ }
+};
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
new file mode 100644
index 00000000000..452885959f6
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -0,0 +1,445 @@
+/*
+ * 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_context.h"
+#include "BKE_editmesh.h"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+
+#include "DNA_ID.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "ED_spreadsheet.h"
+
+#include "bmesh.h"
+
+#include "spreadsheet_data_source_geometry.hh"
+#include "spreadsheet_intern.hh"
+
+namespace blender::ed::spreadsheet {
+
+void GeometryDataSource::foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+{
+ component_->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (meta_data.domain != domain_) {
+ return true;
+ }
+ SpreadsheetColumnID column_id;
+ column_id.name = (char *)name.c_str();
+ fn(column_id);
+ return true;
+ });
+}
+
+std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
+ const SpreadsheetColumnID &column_id) const
+{
+ std::lock_guard lock{mutex_};
+
+ bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
+ if (!attribute) {
+ return {};
+ }
+ const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__);
+ if (attribute.domain != domain_) {
+ return {};
+ }
+ int domain_size = varray->size();
+ const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type());
+ switch (type) {
+ case CD_PROP_FLOAT:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ float value;
+ varray->get(index, &value);
+ r_cell_value.value_float = value;
+ });
+ case CD_PROP_INT32:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ int value;
+ varray->get(index, &value);
+ r_cell_value.value_int = value;
+ });
+ case CD_PROP_BOOL:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ bool value;
+ varray->get(index, &value);
+ r_cell_value.value_bool = value;
+ });
+ case CD_PROP_FLOAT2: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float2 value;
+ varray->get(index, &value);
+ r_cell_value.value_float2 = value;
+ },
+ default_float2_column_width);
+ }
+ case CD_PROP_FLOAT3: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float3 value;
+ varray->get(index, &value);
+ r_cell_value.value_float3 = value;
+ },
+ default_float3_column_width);
+ }
+ case CD_PROP_COLOR: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ ColorGeometry4f value;
+ varray->get(index, &value);
+ r_cell_value.value_color = value;
+ },
+ default_color_column_width);
+ }
+ default:
+ break;
+ }
+ return {};
+}
+
+int GeometryDataSource::tot_rows() const
+{
+ return component_->attribute_domain_size(domain_);
+}
+
+using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
+
+static void get_selected_vertex_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_vertex_indices)
+{
+ for (const int i : IndexRange(mesh.totvert)) {
+ if (is_vertex_selected_fn(i)) {
+ r_vertex_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_corner_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_corner_indices)
+{
+ for (const int i : IndexRange(mesh.totloop)) {
+ const MLoop &loop = mesh.mloop[i];
+ if (is_vertex_selected_fn(loop.v)) {
+ r_corner_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_face_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_face_indices)
+{
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ bool is_selected = true;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ if (!is_vertex_selected_fn(loop.v)) {
+ is_selected = false;
+ break;
+ }
+ }
+ if (is_selected) {
+ r_face_indices.append(poly_index);
+ }
+ }
+}
+
+static void get_selected_edge_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_edge_indices)
+{
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+ if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
+ r_edge_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_indices_on_domain(const Mesh &mesh,
+ const AttributeDomain domain,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_indices)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_FACE:
+ return get_selected_face_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_CORNER:
+ return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_EDGE:
+ return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
+ default:
+ return;
+ }
+}
+
+Span<int64_t> GeometryDataSource::get_selected_element_indices() const
+{
+ std::lock_guard lock{mutex_};
+
+ BLI_assert(object_eval_->mode == OB_MODE_EDIT);
+ BLI_assert(component_->type() == GEO_COMPONENT_TYPE_MESH);
+ Object *object_orig = DEG_get_original_object(object_eval_);
+ Vector<int64_t> &indices = scope_.construct<Vector<int64_t>>(__func__);
+ const MeshComponent *mesh_component = static_cast<const MeshComponent *>(component_);
+ const Mesh *mesh_eval = mesh_component->get_for_read();
+ Mesh *mesh_orig = (Mesh *)object_orig->data;
+ BMesh *bm = mesh_orig->edit_mesh->bm;
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+
+ int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
+ if (orig_indices != nullptr) {
+ /* Use CD_ORIGINDEX layer if it exists. */
+ auto is_vertex_selected = [&](int vertex_index) -> bool {
+ const int i_orig = orig_indices[vertex_index];
+ if (i_orig < 0) {
+ return false;
+ }
+ if (i_orig >= bm->totvert) {
+ return false;
+ }
+ BMVert *vert = bm->vtable[i_orig];
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ };
+ get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, indices);
+ }
+ else if (mesh_eval->totvert == bm->totvert) {
+ /* Use a simple heuristic to match original vertices to evaluated ones. */
+ auto is_vertex_selected = [&](int vertex_index) -> bool {
+ BMVert *vert = bm->vtable[vertex_index];
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ };
+ get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, indices);
+ }
+
+ return indices;
+}
+
+void InstancesDataSource::foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+{
+ if (component_->instances_amount() == 0) {
+ return;
+ }
+
+ SpreadsheetColumnID column_id;
+ column_id.name = (char *)"Name";
+ fn(column_id);
+ for (const char *name : {"Position", "Rotation", "Scale", "ID"}) {
+ column_id.name = (char *)name;
+ fn(column_id);
+ }
+}
+
+std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
+ const SpreadsheetColumnID &column_id) const
+{
+ if (component_->instances_amount() == 0) {
+ return {};
+ }
+
+ const int size = this->tot_rows();
+ if (STREQ(column_id.name, "Name")) {
+ Span<int> reference_handles = component_->instance_reference_handles();
+ Span<InstanceReference> references = component_->references();
+ std::unique_ptr<ColumnValues> values = column_values_from_function(
+ "Name", size, [reference_handles, references](int index, CellValue &r_cell_value) {
+ const InstanceReference &reference = references[reference_handles[index]];
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
+ r_cell_value.value_object = ObjectCellValue{&object};
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ r_cell_value.value_collection = CollectionCellValue{&collection};
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
+ }
+ });
+ values->default_width = 8.0f;
+ return values;
+ }
+ Span<float4x4> transforms = component_->instance_transforms();
+ if (STREQ(column_id.name, "Position")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].translation();
+ },
+ default_float3_column_width);
+ }
+ if (STREQ(column_id.name, "Rotation")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].to_euler();
+ },
+ default_float3_column_width);
+ }
+ if (STREQ(column_id.name, "Scale")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].scale();
+ },
+ default_float3_column_width);
+ }
+ Span<int> ids = component_->instance_ids();
+ if (STREQ(column_id.name, "ID")) {
+ /* Make the column a bit wider by default, since the IDs tend to be large numbers. */
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; },
+ 5.5f);
+ }
+ return {};
+}
+
+int InstancesDataSource::tot_rows() const
+{
+ return component_->instances_amount();
+}
+
+static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
+ Object *object_eval,
+ const GeometryComponentType used_component_type)
+{
+ GeometrySet geometry_set;
+ if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
+ Object *object_orig = DEG_get_original_object(object_eval);
+ if (object_orig->type == OB_MESH) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ if (object_orig->mode == OB_MODE_EDIT) {
+ Mesh *mesh = (Mesh *)object_orig->data;
+ BMEditMesh *em = mesh->edit_mesh;
+ if (em != nullptr) {
+ Mesh *new_mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ /* This is a potentially heavy operation to do on every redraw. The best solution here is
+ * to display the data directly from the bmesh without a conversion, which can be
+ * implemented a bit later. */
+ BM_mesh_bm_to_me_for_eval(em->bm, new_mesh, nullptr);
+ mesh_component.replace(new_mesh, GeometryOwnershipType::Owned);
+ }
+ }
+ else {
+ Mesh *mesh = (Mesh *)object_orig->data;
+ mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
+ }
+ mesh_component.copy_vertex_group_names_from_object(*object_orig);
+ }
+ else if (object_orig->type == OB_POINTCLOUD) {
+ PointCloud *pointcloud = (PointCloud *)object_orig->data;
+ PointCloudComponent &pointcloud_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+ pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
+ }
+ }
+ else if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED) {
+ if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
+ if (mesh == nullptr) {
+ return geometry_set;
+ }
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
+ mesh_component.copy_vertex_group_names_from_object(*object_eval);
+ }
+ else {
+ if (BLI_listbase_count(&sspreadsheet->context_path) == 1) {
+ /* Use final evaluated object. */
+ if (object_eval->runtime.geometry_set_eval != nullptr) {
+ geometry_set = *object_eval->runtime.geometry_set_eval;
+ }
+ }
+ else {
+ if (object_eval->runtime.geometry_set_previews != nullptr) {
+ GHash *ghash = (GHash *)object_eval->runtime.geometry_set_previews;
+ const uint64_t key = ED_spreadsheet_context_path_hash(sspreadsheet);
+ GeometrySet *geometry_set_preview = (GeometrySet *)BLI_ghash_lookup_default(
+ ghash, POINTER_FROM_UINT(key), nullptr);
+ if (geometry_set_preview != nullptr) {
+ geometry_set = *geometry_set_preview;
+ }
+ }
+ }
+ }
+ }
+ return geometry_set;
+}
+
+static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
+ return (GeometryComponentType)sspreadsheet->geometry_component_type;
+ }
+ if (object_eval->type == OB_POINTCLOUD) {
+ return GEO_COMPONENT_TYPE_POINT_CLOUD;
+ }
+ return GEO_COMPONENT_TYPE_MESH;
+}
+
+std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
+ const GeometryComponentType component_type = get_display_component_type(C, object_eval);
+ GeometrySet geometry_set = get_display_geometry_set(sspreadsheet, object_eval, component_type);
+
+ if (!geometry_set.has(component_type)) {
+ return {};
+ }
+
+ if (component_type == GEO_COMPONENT_TYPE_INSTANCES) {
+ return std::make_unique<InstancesDataSource>(geometry_set);
+ }
+ return std::make_unique<GeometryDataSource>(object_eval, geometry_set, component_type, domain);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
new file mode 100644
index 00000000000..273d39f27bf
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -0,0 +1,94 @@
+/*
+ * 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 <mutex>
+
+#include "BLI_resource_scope.hh"
+
+#include "BKE_geometry_set.hh"
+
+#include "spreadsheet_data_source.hh"
+
+struct bContext;
+
+namespace blender::ed::spreadsheet {
+
+class GeometryDataSource : public DataSource {
+ private:
+ Object *object_eval_;
+ const GeometrySet geometry_set_;
+ const GeometryComponent *component_;
+ AttributeDomain domain_;
+
+ /* Some data is computed on the fly only when it is requested. Computing it does not change the
+ * logical state of this data source. Therefore, the corresponding methods are const and need to
+ * be protected with a mutex. */
+ mutable std::mutex mutex_;
+ mutable ResourceScope scope_;
+
+ public:
+ GeometryDataSource(Object *object_eval,
+ GeometrySet geometry_set,
+ const GeometryComponentType component_type,
+ const AttributeDomain domain)
+ : object_eval_(object_eval),
+ geometry_set_(std::move(geometry_set)),
+ component_(geometry_set_.get_component_for_read(component_type)),
+ domain_(domain)
+ {
+ }
+
+ Object *object_eval() const
+ {
+ return object_eval_;
+ }
+
+ Span<int64_t> get_selected_element_indices() const;
+
+ void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+
+ std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const override;
+
+ int tot_rows() const override;
+};
+
+class InstancesDataSource : public DataSource {
+ const GeometrySet geometry_set_;
+ const InstancesComponent *component_;
+
+ public:
+ InstancesDataSource(GeometrySet geometry_set)
+ : geometry_set_(std::move(geometry_set)),
+ component_(geometry_set_.get_component_for_read<InstancesComponent>())
+ {
+ }
+
+ void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+
+ std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const override;
+
+ int tot_rows() const override;
+};
+
+std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
index d6379c740e8..b911c80fa63 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
@@ -142,7 +142,9 @@ static void draw_left_column_content(const int scroll_offset_y,
ARegion *region,
const SpreadsheetDrawer &drawer)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(0, 0, drawer.left_column_width, region->winy - drawer.top_row_height);
uiBlock *left_column_block = UI_block_begin(C, region, __func__, UI_EMBOSS_NONE);
@@ -165,7 +167,7 @@ static void draw_left_column_content(const int scroll_offset_y,
UI_block_end(C, left_column_block);
UI_block_draw(C, left_column_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void draw_top_row_content(const bContext *C,
@@ -173,7 +175,9 @@ static void draw_top_row_content(const bContext *C,
const SpreadsheetDrawer &drawer,
const int scroll_offset_x)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(drawer.left_column_width + 1,
region->winy - drawer.top_row_height,
region->winx - drawer.left_column_width,
@@ -200,7 +204,7 @@ static void draw_top_row_content(const bContext *C,
UI_block_end(C, first_row_block);
UI_block_draw(C, first_row_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void draw_cell_contents(const bContext *C,
@@ -209,7 +213,9 @@ static void draw_cell_contents(const bContext *C,
const int scroll_offset_x,
const int scroll_offset_y)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(drawer.left_column_width + 1,
0,
region->winx - drawer.left_column_width,
@@ -248,7 +254,7 @@ static void draw_cell_contents(const bContext *C,
UI_block_end(C, cells_block);
UI_block_draw(C, cells_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void update_view2d_tot_rect(const SpreadsheetDrawer &drawer,
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
index 6828006f4a1..647587ec8b0 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
@@ -19,7 +19,6 @@
#include "BLI_vector.hh"
struct uiBlock;
-struct rcti;
struct bContext;
struct ARegion;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc
deleted file mode 100644
index 910bc0a34ec..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc
+++ /dev/null
@@ -1,447 +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.
- */
-
-#include "BKE_context.h"
-#include "BKE_editmesh.h"
-#include "BKE_lib_id.h"
-#include "BKE_mesh.h"
-#include "BKE_mesh_wrapper.h"
-#include "BKE_modifier.h"
-
-#include "DNA_ID.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_space_types.h"
-#include "DNA_userdef_types.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "bmesh.h"
-
-#include "spreadsheet_from_geometry.hh"
-#include "spreadsheet_intern.hh"
-
-namespace blender::ed::spreadsheet {
-
-using blender::bke::ReadAttribute;
-using blender::bke::ReadAttributePtr;
-
-static void add_columns_for_instances(const InstancesComponent &instances_component,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources)
-{
- Span<InstancedData> instance_data = instances_component.instanced_data();
- Span<float4x4> transforms = instances_component.transforms();
-
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns =
- resources.construct<Vector<std::unique_ptr<SpreadsheetColumn>>>("columns");
-
- columns.append(spreadsheet_column_from_function(
- "Name", [instance_data](int index, CellValue &r_cell_value) {
- const InstancedData &data = instance_data[index];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- if (data.data.object != nullptr) {
- r_cell_value.value_object = ObjectCellValue{data.data.object};
- }
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- if (data.data.collection != nullptr) {
- r_cell_value.value_collection = CollectionCellValue{data.data.collection};
- }
- }
- }));
-
- columns.last()->default_width = 8.0f;
-
- static std::array<char, 3> axis_char = {'X', 'Y', 'Z'};
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Position ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].translation()[i];
- }));
- }
-
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Rotation ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].to_euler()[i];
- }));
- }
-
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Scale ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].scale()[i];
- }));
- }
-
- for (std::unique_ptr<SpreadsheetColumn> &column : columns) {
- column_layout.columns.append(column.get());
- }
-
- column_layout.row_indices = instance_data.index_range().as_span();
- column_layout.tot_rows = instances_component.instances_amount();
-}
-
-static Vector<std::string> get_sorted_attribute_names_to_display(
- const GeometryComponent &component, const AttributeDomain domain)
-{
- Vector<std::string> attribute_names;
- component.attribute_foreach(
- [&](const StringRef attribute_name, const AttributeMetaData &meta_data) {
- if (meta_data.domain == domain) {
- attribute_names.append(attribute_name);
- }
- return true;
- });
- std::sort(attribute_names.begin(),
- attribute_names.end(),
- [](const std::string &a, const std::string &b) {
- return BLI_strcasecmp_natural(a.c_str(), b.c_str()) < 0;
- });
- return attribute_names;
-}
-
-static void add_columns_for_attribute(const ReadAttribute *attribute,
- const StringRefNull attribute_name,
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns)
-{
- const CustomDataType data_type = attribute->custom_data_type();
- switch (data_type) {
- case CD_PROP_FLOAT: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- float value;
- attribute->get(index, &value);
- r_cell_value.value_float = value;
- }));
- break;
- }
- case CD_PROP_FLOAT2: {
- static std::array<char, 2> axis_char = {'X', 'Y'};
- for (const int i : {0, 1}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- float2 value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_FLOAT3: {
- static std::array<char, 3> axis_char = {'X', 'Y', 'Z'};
- for (const int i : {0, 1, 2}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- float3 value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_COLOR: {
- static std::array<char, 4> axis_char = {'R', 'G', 'B', 'A'};
- for (const int i : {0, 1, 2, 3}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- Color4f value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_INT32: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- int value;
- attribute->get(index, &value);
- r_cell_value.value_int = value;
- }));
- break;
- }
- case CD_PROP_BOOL: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- bool value;
- attribute->get(index, &value);
- r_cell_value.value_bool = value;
- }));
- break;
- }
- default:
- break;
- }
-}
-
-static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
- Object *object_eval,
- const GeometryComponentType used_component_type)
-{
- GeometrySet geometry_set;
- if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
- if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
- Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
- if (mesh == nullptr) {
- return geometry_set;
- }
- BKE_mesh_wrapper_ensure_mdata(mesh);
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
- mesh_component.copy_vertex_group_names_from_object(*object_eval);
- }
- else {
- if (object_eval->runtime.geometry_set_eval != nullptr) {
- /* This does not copy the geometry data itself. */
- geometry_set = *object_eval->runtime.geometry_set_eval;
- }
- }
- }
- else {
- Object *object_orig = DEG_get_original_object(object_eval);
- if (object_orig->type == OB_MESH) {
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- if (object_orig->mode == OB_MODE_EDIT) {
- Mesh *mesh = (Mesh *)object_orig->data;
- BMEditMesh *em = mesh->edit_mesh;
- if (em != nullptr) {
- Mesh *new_mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- /* This is a potentially heavy operation to do on every redraw. The best solution here is
- * to display the data directly from the bmesh without a conversion, which can be
- * implemented a bit later. */
- BM_mesh_bm_to_me_for_eval(em->bm, new_mesh, nullptr);
- mesh_component.replace(new_mesh, GeometryOwnershipType::Owned);
- }
- }
- else {
- Mesh *mesh = (Mesh *)object_orig->data;
- mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
- }
- mesh_component.copy_vertex_group_names_from_object(*object_orig);
- }
- else if (object_orig->type == OB_POINTCLOUD) {
- PointCloud *pointcloud = (PointCloud *)object_orig->data;
- PointCloudComponent &pointcloud_component =
- geometry_set.get_component_for_write<PointCloudComponent>();
- pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
- }
- }
- return geometry_set;
-}
-
-using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
-
-static void get_selected_vertex_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_vertex_indices)
-{
- for (const int i : IndexRange(mesh.totvert)) {
- if (is_vertex_selected_fn(i)) {
- r_vertex_indices.append(i);
- }
- }
-}
-
-static void get_selected_corner_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_corner_indices)
-{
- for (const int i : IndexRange(mesh.totloop)) {
- const MLoop &loop = mesh.mloop[i];
- if (is_vertex_selected_fn(loop.v)) {
- r_corner_indices.append(i);
- }
- }
-}
-
-static void get_selected_face_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_face_indices)
-{
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
- bool is_selected = true;
- for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
- if (!is_vertex_selected_fn(loop.v)) {
- is_selected = false;
- break;
- }
- }
- if (is_selected) {
- r_face_indices.append(poly_index);
- }
- }
-}
-
-static void get_selected_edge_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_edge_indices)
-{
- for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
- if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
- r_edge_indices.append(i);
- }
- }
-}
-
-static void get_selected_indices_on_domain(const Mesh &mesh,
- const AttributeDomain domain,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_indices)
-{
- switch (domain) {
- case ATTR_DOMAIN_POINT:
- return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_FACE:
- return get_selected_face_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_CORNER:
- return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_EDGE:
- return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
- default:
- return;
- }
-}
-
-static Span<int64_t> filter_mesh_elements_by_selection(const bContext *C,
- Object *object_eval,
- const MeshComponent *component,
- const AttributeDomain domain,
- ResourceCollector &resources)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- const bool show_only_selected = sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY;
- if (object_eval->mode == OB_MODE_EDIT && show_only_selected) {
- Object *object_orig = DEG_get_original_object(object_eval);
- Vector<int64_t> &visible_rows = resources.construct<Vector<int64_t>>("visible rows");
- const Mesh *mesh_eval = component->get_for_read();
- Mesh *mesh_orig = (Mesh *)object_orig->data;
- BMesh *bm = mesh_orig->edit_mesh->bm;
- BM_mesh_elem_table_ensure(bm, BM_VERT);
-
- int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
- if (orig_indices != nullptr) {
- /* Use CD_ORIGINDEX layer if it exists. */
- auto is_vertex_selected = [&](int vertex_index) -> bool {
- const int i_orig = orig_indices[vertex_index];
- if (i_orig < 0) {
- return false;
- }
- if (i_orig >= bm->totvert) {
- return false;
- }
- BMVert *vert = bm->vtable[i_orig];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- };
- get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
- }
- else if (mesh_eval->totvert == bm->totvert) {
- /* Use a simple heuristic to match original vertices to evaluated ones. */
- auto is_vertex_selected = [&](int vertex_index) -> bool {
- BMVert *vert = bm->vtable[vertex_index];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- };
- get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
- }
- /* This is safe, because the vector lives in the resource collector. */
- return visible_rows.as_span();
- }
- /* No filter is used. */
- const int domain_size = component->attribute_domain_size(domain);
- return IndexRange(domain_size).as_span();
-}
-
-static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
- return (GeometryComponentType)sspreadsheet->geometry_component_type;
- }
- if (object_eval->type == OB_POINTCLOUD) {
- return GEO_COMPONENT_TYPE_POINT_CLOUD;
- }
- return GEO_COMPONENT_TYPE_MESH;
-}
-
-void spreadsheet_columns_from_geometry(const bContext *C,
- Object *object_eval,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
- const GeometryComponentType component_type = get_display_component_type(C, object_eval);
-
- /* Create a resource collector that owns stuff that needs to live until drawing is done. */
- GeometrySet &geometry_set = resources.add_value(
- get_display_geometry_set(sspreadsheet, object_eval, component_type), "geometry set");
-
- const GeometryComponent *component = geometry_set.get_component_for_read(component_type);
- if (component == nullptr) {
- return;
- }
- if (component_type == GEO_COMPONENT_TYPE_INSTANCES) {
- add_columns_for_instances(
- *static_cast<const InstancesComponent *>(component), column_layout, resources);
- return;
- }
-
- if (!component->attribute_domain_supported(domain)) {
- return;
- }
-
- Vector<std::string> attribute_names = get_sorted_attribute_names_to_display(*component, domain);
-
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns =
- resources.construct<Vector<std::unique_ptr<SpreadsheetColumn>>>("columns");
-
- for (StringRefNull attribute_name : attribute_names) {
- ReadAttributePtr attribute_ptr = component->attribute_try_get_for_read(attribute_name);
- ReadAttribute &attribute = *attribute_ptr;
- resources.add(std::move(attribute_ptr), "attribute");
- add_columns_for_attribute(&attribute, attribute_name, columns);
- }
-
- for (std::unique_ptr<SpreadsheetColumn> &column : columns) {
- column_layout.columns.append(column.get());
- }
-
- /* The filter below only works for mesh vertices currently. */
- Span<int64_t> visible_rows;
- if (component_type == GEO_COMPONENT_TYPE_MESH) {
- visible_rows = filter_mesh_elements_by_selection(
- C, object_eval, static_cast<const MeshComponent *>(component), domain, resources);
- }
- else {
- visible_rows = IndexRange(component->attribute_domain_size(domain)).as_span();
- }
-
- const int domain_size = component->attribute_domain_size(domain);
- column_layout.row_indices = visible_rows;
- column_layout.tot_rows = domain_size;
-}
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
new file mode 100644
index 00000000000..8079763a339
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -0,0 +1,256 @@
+/*
+ * 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 <iomanip>
+#include <sstream>
+
+#include "spreadsheet_layout.hh"
+
+#include "DNA_userdef_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BLF_api.h"
+
+namespace blender::ed::spreadsheet {
+
+class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
+ private:
+ const SpreadsheetLayout &spreadsheet_layout_;
+
+ public:
+ SpreadsheetLayoutDrawer(const SpreadsheetLayout &spreadsheet_layout)
+ : spreadsheet_layout_(spreadsheet_layout)
+ {
+ tot_columns = spreadsheet_layout.columns.size();
+ tot_rows = spreadsheet_layout.row_indices.size();
+ left_column_width = spreadsheet_layout.index_column_width;
+ }
+
+ void draw_top_row_cell(int column_index, const CellDrawParams &params) const final
+ {
+ const StringRefNull name = spreadsheet_layout_.columns[column_index].values->name();
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ name.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Center-align column headers. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_RIGHT);
+ }
+
+ void draw_left_column_cell(int row_index, const CellDrawParams &params) const final
+ {
+ const int real_index = spreadsheet_layout_.row_indices[row_index];
+ std::string index_str = std::to_string(real_index);
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ index_str.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align indices. */
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ }
+
+ void draw_content_cell(int row_index, int column_index, const CellDrawParams &params) const final
+ {
+ const int real_index = spreadsheet_layout_.row_indices[row_index];
+ const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
+ CellValue cell_value;
+ column.get_value(real_index, cell_value);
+
+ if (cell_value.value_int.has_value()) {
+ const int value = *cell_value.value_int;
+ const std::string value_str = std::to_string(value);
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ value_str.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align Integers. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ }
+ else if (cell_value.value_float.has_value()) {
+ const float value = *cell_value.value_float;
+ std::stringstream ss;
+ ss << std::fixed << std::setprecision(3) << value;
+ const std::string value_str = ss.str();
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ value_str.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align Floats. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ }
+ else if (cell_value.value_bool.has_value()) {
+ const bool value = *cell_value.value_bool;
+ const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ icon,
+ "",
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
+ }
+ else if (cell_value.value_float2.has_value()) {
+ const float2 value = *cell_value.value_float2;
+ this->draw_float_vector(params, Span(&value.x, 2));
+ }
+ else if (cell_value.value_float3.has_value()) {
+ const float3 value = *cell_value.value_float3;
+ this->draw_float_vector(params, Span(&value.x, 3));
+ }
+ else if (cell_value.value_color.has_value()) {
+ const ColorGeometry4f value = *cell_value.value_color;
+ this->draw_float_vector(params, Span(&value.r, 4));
+ }
+ else if (cell_value.value_object.has_value()) {
+ const ObjectCellValue value = *cell_value.value_object;
+ uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_OBJECT_DATA,
+ reinterpret_cast<const ID *const>(value.object)->name + 2,
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ }
+ else if (cell_value.value_collection.has_value()) {
+ const CollectionCellValue value = *cell_value.value_collection;
+ uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_OUTLINER_COLLECTION,
+ reinterpret_cast<const ID *const>(value.collection)->name + 2,
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ }
+ }
+
+ void draw_float_vector(const CellDrawParams &params, const Span<float> values) const
+ {
+ BLI_assert(!values.is_empty());
+ const float segment_width = (float)params.width / values.size();
+ for (const int i : values.index_range()) {
+ std::stringstream ss;
+ const float value = values[i];
+ ss << std::fixed << std::setprecision(3) << value;
+ const std::string value_str = ss.str();
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ value_str.c_str(),
+ params.xmin + i * segment_width,
+ params.ymin,
+ segment_width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align Floats. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ }
+ }
+
+ int column_width(int column_index) const final
+ {
+ return spreadsheet_layout_.columns[column_index].width;
+ }
+};
+
+std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_layout(
+ const SpreadsheetLayout &spreadsheet_layout)
+{
+ return std::make_unique<SpreadsheetLayoutDrawer>(spreadsheet_layout);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh b/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh
new file mode 100644
index 00000000000..1768af6ae09
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "spreadsheet_column_values.hh"
+#include "spreadsheet_draw.hh"
+
+namespace blender::ed::spreadsheet {
+
+/* Layout information for a single column. */
+struct ColumnLayout {
+ const ColumnValues *values;
+ int width;
+};
+
+/* Layout information for the entire spreadsheet. */
+struct SpreadsheetLayout {
+ Vector<ColumnLayout> columns;
+ Span<int64_t> row_indices;
+ int index_column_width = 100;
+};
+
+std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_layout(
+ const SpreadsheetLayout &spreadsheet_layout);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index 98faf89f8ae..af783051661 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -138,13 +138,7 @@ static void text_listener(const wmSpaceTypeListenerParams *params)
switch (wmn->data) {
case ND_DISPLAY:
- ED_area_tag_redraw(area);
- break;
case ND_CURSOR:
- if (st->text && st->text == wmn->reference) {
- text_scroll_to_cursor__area(st, area, true);
- }
-
ED_area_tag_redraw(area);
break;
}
@@ -160,13 +154,8 @@ static void text_listener(const wmSpaceTypeListenerParams *params)
ATTR_FALLTHROUGH; /* fall down to tag redraw */
case NA_ADDED:
case NA_REMOVED:
- ED_area_tag_redraw(area);
- break;
case NA_SELECTED:
- if (st->text && st->text == wmn->reference) {
- text_scroll_to_cursor__area(st, area, true);
- }
-
+ ED_area_tag_redraw(area);
break;
}
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index 8b8034124d9..17831c95575 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -1739,7 +1739,7 @@ void text_update_character_width(SpaceText *st)
/* Moves the view to the cursor location,
* also used to make sure the view isn't outside the file */
-void text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center)
+void ED_text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center)
{
Text *text;
int i, x, winx = region->winx;
@@ -1818,7 +1818,7 @@ void text_scroll_to_cursor__area(SpaceText *st, ScrArea *area, const bool center
region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (region) {
- text_scroll_to_cursor(st, region, center);
+ ED_text_scroll_to_cursor(st, region, center);
}
}
diff --git a/source/blender/editors/space_text/text_intern.h b/source/blender/editors/space_text/text_intern.h
index a33af56e11a..241e0133a8a 100644
--- a/source/blender/editors/space_text/text_intern.h
+++ b/source/blender/editors/space_text/text_intern.h
@@ -39,7 +39,6 @@ void draw_text_main(struct SpaceText *st, struct ARegion *region);
void text_update_line_edited(struct TextLine *line);
void text_update_edited(struct Text *text);
void text_update_character_width(struct SpaceText *st);
-void text_scroll_to_cursor(struct SpaceText *st, struct ARegion *region, const bool center);
void text_scroll_to_cursor__area(struct SpaceText *st, struct ScrArea *area, const bool center);
void text_update_cursor_moved(struct bContext *C);
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 526285c076a..9ec759ce4ae 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -266,8 +266,6 @@ static int text_new_exec(bContext *C, wmOperator *UNUSED(op))
PropertyRNA *prop;
text = BKE_text_add(bmain, "Text");
- /* Texts have no user by default... Only the 'real' user flag. */
- id_us_min(&text->id);
/* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
@@ -506,12 +504,10 @@ static int text_unlink_exec(bContext *C, wmOperator *UNUSED(op))
if (text->id.prev) {
st->text = text->id.prev;
text_update_cursor_moved(C);
- WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
}
else if (text->id.next) {
st->text = text->id.next;
text_update_cursor_moved(C);
- WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
}
}
@@ -3200,7 +3196,7 @@ static void text_cursor_set_apply(bContext *C, wmOperator *op, const wmEvent *ev
if (event->type == TIMER) {
text_cursor_set_to_pos(st, region, event->mval[0], event->mval[1], 1);
- text_scroll_to_cursor(st, region, false);
+ ED_text_scroll_to_cursor(st, region, false);
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
}
}
@@ -3210,7 +3206,7 @@ static void text_cursor_set_apply(bContext *C, wmOperator *op, const wmEvent *ev
if (event->type == TIMER) {
text_cursor_set_to_pos(
st, region, CLAMPIS(event->mval[0], 0, region->winx), event->mval[1], 1);
- text_scroll_to_cursor(st, region, false);
+ ED_text_scroll_to_cursor(st, region, false);
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
}
}
@@ -3219,7 +3215,7 @@ static void text_cursor_set_apply(bContext *C, wmOperator *op, const wmEvent *ev
if (event->type != TIMER) {
text_cursor_set_to_pos(st, region, event->mval[0], event->mval[1], 1);
- text_scroll_to_cursor(st, region, false);
+ ED_text_scroll_to_cursor(st, region, false);
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
ssel->mval_prev[0] = event->mval[0];
diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c
index d4692f156d3..ceba8ca268d 100644
--- a/source/blender/editors/space_userpref/space_userpref.c
+++ b/source/blender/editors/space_userpref/space_userpref.c
@@ -79,7 +79,7 @@ static SpaceLink *userpref_create(const ScrArea *area, const Scene *UNUSED(scene
BLI_addtail(&spref->regionbase, region);
region->regiontype = RGN_TYPE_EXECUTE;
region->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
- region->flag |= RGN_FLAG_DYNAMIC_SIZE;
+ region->flag |= RGN_FLAG_DYNAMIC_SIZE | RGN_FLAG_HIDDEN;
/* main region */
region = MEM_callocN(sizeof(ARegion), "main region for userpref");
@@ -251,6 +251,7 @@ void ED_spacetype_userpref(void)
/* regions: execution window */
art = MEM_callocN(sizeof(ARegionType), "spacetype userpref region");
art->regionid = RGN_TYPE_EXECUTE;
+ art->prefsizey = HEADERY;
art->init = userpref_execute_region_init;
art->layout = ED_region_panels_layout;
art->draw = ED_region_panels_draw;
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 6969ecf197e..660ae9da506 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -128,7 +128,7 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
GPU_front_facing(ob->transflag & OB_NEG_SCALE);
- /* Just to create the data to pass to immediate mode, grr! */
+ /* Just to create the data to pass to immediate mode! (sigh) */
const int *facemap_data = CustomData_get_layer(&me->pdata, CD_FACEMAP);
if (facemap_data) {
GPU_blend(GPU_BLEND_ALPHA);
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index e6916c34a88..001def7318e 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -625,6 +625,7 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop)
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB);
RNA_string_set(drop->ptr, "name", id->name + 2);
+ RNA_boolean_set(drop->ptr, "duplicate", false);
}
static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop)
@@ -1595,7 +1596,6 @@ static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(area))
}
const char *view3d_context_dir[] = {
- "active_base",
"active_object",
NULL,
};
@@ -1608,20 +1608,6 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
if (CTX_data_dir(member)) {
CTX_data_dir_set(result, view3d_context_dir);
}
- else if (CTX_data_equals(member, "active_base")) {
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
- if (view_layer->basact) {
- Object *ob = view_layer->basact->object;
- /* if hidden but in edit mode, we still display, can happen with animation */
- if ((view_layer->basact->flag & BASE_VISIBLE_DEPSGRAPH) != 0 ||
- (ob->mode != OB_MODE_OBJECT)) {
- CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, view_layer->basact);
- }
- }
-
- return 1;
- }
else if (CTX_data_equals(member, "active_object")) {
/* In most cases the active object is the `view_layer->basact->object`.
* For the 3D view however it can be NULL when hidden.
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index 17f60dfc210..e42f6b5faac 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1031,7 +1031,9 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float
}
}
}
- BKE_nurb_test_2d(nu);
+ if (CU_IS_2D(cu)) {
+ BKE_nurb_project_2d(nu);
+ }
BKE_nurb_handles_test(nu, true, false); /* test for bezier too */
}
}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index a46d093c039..4a595c716b6 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -778,7 +778,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region,
}
/* draw */
- immUniformThemeColorShade(TH_VIEW_OVERLAY, 100);
+ immUniformThemeColorShadeAlpha(TH_VIEW_OVERLAY, 100, 255);
/* TODO Was using:
* UI_draw_roundbox_4fv(false, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 2.0f, color);
@@ -832,52 +832,6 @@ static void drawrenderborder(ARegion *region, View3D *v3d)
immUnbindProgram();
}
-void ED_view3d_draw_depth(Depsgraph *depsgraph, ARegion *region, View3D *v3d, bool alphaoverride)
-{
- struct bThemeState theme_state;
- Scene *scene = DEG_get_evaluated_scene(depsgraph);
- RegionView3D *rv3d = region->regiondata;
-
- short flag = v3d->flag;
- float glalphaclip = U.glalphaclip;
- /* temp set drawtype to solid */
- /* Setting these temporarily is not nice */
- v3d->flag &= ~V3D_SELECT_OUTLINE;
-
- /* not that nice but means we wont zoom into billboards */
- U.glalphaclip = alphaoverride ? 0.5f : glalphaclip;
-
- /* Tools may request depth outside of regular drawing code. */
- UI_Theme_Store(&theme_state);
- UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
-
- ED_view3d_draw_setup_view(
- G_MAIN->wm.first, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
-
- /* get surface depth without bias */
- rv3d->rflag |= RV3D_ZOFFSET_DISABLED;
-
- /* Needed in cases the 3D Viewport isn't already setup. */
- WM_draw_region_viewport_ensure(region, SPACE_VIEW3D);
- WM_draw_region_viewport_bind(region);
-
- GPUViewport *viewport = WM_draw_region_get_viewport(region);
- /* When Blender is starting, a click event can trigger a depth test while the viewport is not
- * yet available. */
- if (viewport != NULL) {
- DRW_draw_depth_loop(depsgraph, region, v3d, viewport, false);
- }
-
- WM_draw_region_viewport_unbind(region);
-
- rv3d->rflag &= ~RV3D_ZOFFSET_DISABLED;
-
- U.glalphaclip = glalphaclip;
- v3d->flag = flag;
-
- UI_Theme_Restore(&theme_state);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1334,6 +1288,11 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y
name_array[name_array_len++] = IFACE_(" (Local)");
}
+ /* Indicate that clipping region is enabled. */
+ if (rv3d->rflag & RV3D_CLIPPING) {
+ name_array[name_array_len++] = IFACE_(" (Clipped)");
+ }
+
if (name_array_len > 1) {
BLI_string_join_array(tmpstr, sizeof(tmpstr), name_array, name_array_len);
name = tmpstr;
@@ -1634,7 +1593,8 @@ void view3d_main_region_draw(const bContext *C, ARegion *region)
/* No depth test for drawing action zones afterwards. */
GPU_depth_test(GPU_DEPTH_NONE);
- v3d->flag |= V3D_INVALID_BACKBUF;
+ v3d->runtime.flag &= ~V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
+ /* TODO: Clear cache? */
}
/** \} */
@@ -2062,7 +2022,6 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
source_shading_settings = shading_override;
}
memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading));
- v3d.shading.type = drawtype;
if (drawtype == OB_MATERIAL) {
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
@@ -2072,8 +2031,17 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
v3d.shading.flag = V3D_SHADING_SCENE_WORLD_RENDER | V3D_SHADING_SCENE_LIGHTS_RENDER;
v3d.shading.render_pass = SCE_PASS_COMBINED;
}
+ else if (drawtype == OB_TEXTURE) {
+ drawtype = OB_SOLID;
+ v3d.shading.light = V3D_LIGHTING_STUDIO;
+ v3d.shading.color_type = V3D_SHADING_TEXTURE_COLOR;
+ }
+ v3d.shading.type = drawtype;
v3d.flag2 = V3D_HIDE_OVERLAYS;
+ /* HACK: When rendering gpencil objects this opacity is used to mix vertex colors in when not in
+ * render mode. */
+ v3d.overlay.gpencil_vertex_paint_opacity = 1.0f;
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
v3d.flag2 |= V3D_SHOW_ANNOTATION;
@@ -2112,7 +2080,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
return ED_view3d_draw_offscreen_imbuf(depsgraph,
scene,
- drawtype,
+ v3d.shading.type,
&v3d,
&region,
width,
@@ -2146,8 +2114,15 @@ static bool view3d_clipping_test(const float co[3], const float clip[6][4])
return true;
}
-/* For 'local' ED_view3d_clipping_local must run first
- * then all comparisons can be done in localspace. */
+/**
+ * Return true when `co` is hidden by the 3D views clipping planes.
+ *
+ * \param local: When true use local (object-space) #ED_view3d_clipping_local must run first,
+ * then all comparisons can be done in local-space.
+ * \return True when `co` is outside all clipping planes.
+ *
+ * \note Callers should check #RV3D_CLIPPING_ENABLED first.
+ */
bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], const bool is_local)
{
return view3d_clipping_test(co, is_local ? rv3d->clip_local : rv3d->clip);
@@ -2168,6 +2143,10 @@ static void validate_object_select_id(struct Depsgraph *depsgraph,
View3D *v3d,
Object *obact)
{
+ /* TODO: Use a flag in the selection engine itself. */
+ if (v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN) {
+ return;
+ }
Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact);
BLI_assert(region->regiontype == RGN_TYPE_WINDOW);
@@ -2186,11 +2165,7 @@ static void validate_object_select_id(struct Depsgraph *depsgraph,
/* do nothing */
}
else {
- v3d->flag &= ~V3D_INVALID_BACKBUF;
- return;
- }
-
- if (!(v3d->flag & V3D_INVALID_BACKBUF)) {
+ v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
return;
}
@@ -2199,9 +2174,7 @@ static void validate_object_select_id(struct Depsgraph *depsgraph,
DRW_select_buffer_context_create(&base, 1, -1);
}
- /* TODO: Create a flag in `DRW_manager` because the drawing is no longer
- * made on the back-buffer in this case. */
- v3d->flag &= ~V3D_INVALID_BACKBUF;
+ v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
}
/* TODO: Creating, attaching texture, and destroying a framebuffer is quite slow.
@@ -2228,26 +2201,7 @@ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void
void ED_view3d_select_id_validate(ViewContext *vc)
{
- /* TODO: Create a flag in `DRW_manager` because the drawing is no longer
- * made on the back-buffer in this case. */
- if (vc->v3d->flag & V3D_INVALID_BACKBUF) {
- validate_object_select_id(vc->depsgraph, vc->view_layer, vc->region, vc->v3d, vc->obact);
- }
-}
-
-void ED_view3d_backbuf_depth_validate(ViewContext *vc)
-{
- if (vc->v3d->flag & V3D_INVALID_BACKBUF) {
- ARegion *region = vc->region;
- Object *obact_eval = DEG_get_evaluated_object(vc->depsgraph, vc->obact);
-
- if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE_DEPSGRAPH) != 0)) {
- GPUViewport *viewport = WM_draw_region_get_viewport(region);
- DRW_draw_depth_object(vc->scene, vc->region, vc->v3d, viewport, obact_eval);
- }
-
- vc->v3d->flag &= ~V3D_INVALID_BACKBUF;
- }
+ validate_object_select_id(vc->depsgraph, vc->view_layer, vc->region, vc->v3d, vc->obact);
}
/**
@@ -2319,7 +2273,7 @@ void view3d_update_depths_rect(ARegion *region, ViewDepths *d, rcti *rect)
}
/* Note, with nouveau drivers the glReadPixels() is very slow. T24339. */
-void ED_view3d_depth_update(ARegion *region)
+static void view3d_depth_cache_update(ARegion *region)
{
RegionView3D *rv3d = region->regiondata;
@@ -2341,13 +2295,9 @@ void ED_view3d_depth_update(ARegion *region)
if (d->damaged) {
GPUViewport *viewport = WM_draw_region_get_viewport(region);
- rcti r = {
- .xmin = 0,
- .xmax = d->w,
- .ymin = 0,
- .ymax = d->h,
- };
- view3d_opengl_read_Z_pixels(viewport, &r, d->depths);
+ DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport);
+ GPU_framebuffer_read_depth(fbl->depth_only_fb, 0, 0, d->w, d->h, GPU_DATA_FLOAT, d->depths);
+
/* Assumed to be this as they are never changed. */
d->depth_range[0] = 0.0;
d->depth_range[1] = 1.0;
@@ -2380,19 +2330,81 @@ float view3d_depth_near(ViewDepths *d)
return far == far_real ? FLT_MAX : far;
}
-void ED_view3d_draw_depth_gpencil(Depsgraph *depsgraph, Scene *scene, ARegion *region, View3D *v3d)
+/**
+ * Redraw the viewport depth buffer.
+ *
+ * \param mode: V3D_DEPTH_NO_GPENCIL - Redraw viewport without Grease Pencil and Annotations.
+ * V3D_DEPTH_GPENCIL_ONLY - Redraw viewport with Grease Pencil and Annotations only.
+ * V3D_DEPTH_OBJECT_ONLY - Redraw viewport with active object only.
+ * \param update_cache: If true, store the entire depth buffer in #rv3d->depths.
+ */
+void ED_view3d_depth_override(Depsgraph *depsgraph,
+ ARegion *region,
+ View3D *v3d,
+ Object *obact,
+ eV3DDepthOverrideMode mode,
+ bool update_cache)
{
- /* Setup view matrix. */
- ED_view3d_draw_setup_view(NULL, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
+ if (v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN) {
+ return;
+ }
+ struct bThemeState theme_state;
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ RegionView3D *rv3d = region->regiondata;
- GPU_clear_depth(1.0f);
+ short flag = v3d->flag;
+ /* temp set drawtype to solid */
+ /* Setting these temporarily is not nice */
+ v3d->flag &= ~V3D_SELECT_OUTLINE;
- GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ /* Tools may request depth outside of regular drawing code. */
+ UI_Theme_Store(&theme_state);
+ UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
+
+ ED_view3d_draw_setup_view(
+ G_MAIN->wm.first, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
+
+ /* get surface depth without bias */
+ rv3d->rflag |= RV3D_ZOFFSET_DISABLED;
+
+ /* Needed in cases the 3D Viewport isn't already setup. */
+ WM_draw_region_viewport_ensure(region, SPACE_VIEW3D);
+ WM_draw_region_viewport_bind(region);
GPUViewport *viewport = WM_draw_region_get_viewport(region);
- DRW_draw_depth_loop_gpencil(depsgraph, region, v3d, viewport);
+ /* When Blender is starting, a click event can trigger a depth test while the viewport is not
+ * yet available. */
+ if (viewport != NULL) {
+ switch (mode) {
+ case V3D_DEPTH_NO_GPENCIL:
+ DRW_draw_depth_loop(depsgraph, region, v3d, viewport);
+ break;
+ case V3D_DEPTH_GPENCIL_ONLY:
+ DRW_draw_depth_loop_gpencil(depsgraph, region, v3d, viewport);
+ break;
+ case V3D_DEPTH_OBJECT_ONLY:
+ DRW_draw_depth_object(
+ scene, region, v3d, viewport, DEG_get_evaluated_object(depsgraph, obact));
+ break;
+ }
- GPU_depth_test(GPU_DEPTH_NONE);
+ if (rv3d->depths != NULL) {
+ rv3d->depths->damaged = true;
+ /* TODO: Clear cache? */
+ }
+ if (update_cache) {
+ view3d_depth_cache_update(region);
+ }
+ }
+
+ WM_draw_region_viewport_unbind(region);
+
+ rv3d->rflag &= ~RV3D_ZOFFSET_DISABLED;
+
+ v3d->flag = flag;
+ v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
+
+ UI_Theme_Restore(&theme_state);
}
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index dc590833368..8b6d0e9ee04 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -2507,7 +2507,7 @@ static void view_dolly_to_vector_3d(ARegion *region,
madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac));
}
-static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const short zoom_invert)
+static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert)
{
float zfac = 1.0;
@@ -3628,9 +3628,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
ED_view3d_dist_range_get(v3d, dist_range);
- /* Get Z Depths, needed for perspective, nice for ortho */
- ED_view3d_draw_depth(CTX_data_ensure_evaluated_depsgraph(C), region, v3d, true);
-
+ ED_view3d_depth_override(
+ CTX_data_ensure_evaluated_depsgraph(C), region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
{
/* avoid allocating the whole depth buffer */
ViewDepths depth_temp = {0};
@@ -3667,8 +3666,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
/* convert border to 3d coordinates */
- if ((!ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) ||
- (!ED_view3d_unproject(region, rect.xmin, rect.ymin, depth_close, p_corner))) {
+ if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) ||
+ (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) {
return OPERATOR_CANCELLED;
}
@@ -3691,7 +3690,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
new_dist = rv3d->dist;
/* convert the drawn rectangle into 3d space */
- if (depth_close != FLT_MAX && ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) {
+ if (depth_close != FLT_MAX &&
+ ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) {
negate_v3_v3(new_ofs, p);
}
else {
@@ -5066,7 +5066,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
mval_fl,
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
index 6fa974cdb09..cd0576f2d21 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
@@ -80,32 +80,32 @@ static struct NavigateGizmoInfo g_navigate_params[GZ_INDEX_TOTAL] = {
{
.opname = "VIEW3D_OT_move",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_PAN,
+ .icon = ICON_VIEW_PAN,
},
{
.opname = "VIEW3D_OT_rotate",
.gizmo = "VIEW3D_GT_navigate_rotate",
- 0,
+ .icon = ICON_NONE,
},
{
.opname = "VIEW3D_OT_zoom",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_ZOOM,
+ .icon = ICON_VIEW_ZOOM,
},
{
.opname = "VIEW3D_OT_view_persportho",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_PERSPECTIVE,
+ .icon = ICON_VIEW_PERSPECTIVE,
},
{
.opname = "VIEW3D_OT_view_persportho",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_ORTHO,
+ .icon = ICON_VIEW_ORTHO,
},
{
.opname = "VIEW3D_OT_view_camera",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_CAMERA,
+ .icon = ICON_VIEW_CAMERA,
},
};
@@ -116,7 +116,7 @@ struct NavigateWidgetGroup {
rcti rect_visible;
struct {
char is_persp;
- char is_camera;
+ bool is_camera;
char viewlock;
} rv3d;
} state;
@@ -177,7 +177,7 @@ static void WIDGETGROUP_navigate_setup(const bContext *C, wmGizmoGroup *gzgroup)
/* may be overwritten later */
gz->scale_basis = GIZMO_MINI_SIZE / 2.0f;
- if (info->icon != 0) {
+ if (info->icon != ICON_NONE) {
PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
RNA_property_enum_set(gz->ptr, prop, info->icon);
RNA_enum_set(
@@ -250,6 +250,9 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g
const rcti *rect_visible = ED_region_visible_rect(region);
+ /* Ensure types match so bits are never lost on assignment. */
+ CHECK_TYPE_PAIR(navgroup->state.rv3d.viewlock, rv3d->viewlock);
+
if ((navgroup->state.rect_visible.xmax == rect_visible->xmax) &&
(navgroup->state.rect_visible.ymax == rect_visible->ymax) &&
(navgroup->state.rv3d.is_persp == rv3d->is_persp) &&
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
index 4ac16e8fbe8..05ea35f114f 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
@@ -372,7 +372,7 @@ static int gizmo_axis_cursor_get(wmGizmo *UNUSED(gz))
return WM_CURSOR_DEFAULT;
}
-static void gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
+static bool gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
{
ScrArea *area = CTX_wm_area(C);
const float rad = WIDGET_RADIUS;
@@ -380,6 +380,7 @@ static void gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bound
r_bounding_box->ymin = gz->matrix_basis[3][1] + area->totrct.ymin - rad;
r_bounding_box->xmax = r_bounding_box->xmin + rad;
r_bounding_box->ymax = r_bounding_box->ymin + rad;
+ return true;
}
void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt)
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
index 298a2a7a824..07c3b6bd1d8 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
@@ -154,10 +154,10 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
* Only pre-select a vertex when the cursor is really close to it. */
if (eve_test) {
BMVert *vert = (BMVert *)eve_test;
- float vert_p_co[3], vert_co[3];
+ float vert_p_co[2], vert_co[3];
const float mval_f[2] = {UNPACK2(vc.mval)};
mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co);
- ED_view3d_project(vc.region, vert_co, vert_p_co);
+ ED_view3d_project_v2(vc.region, vert_co, vert_p_co);
float len = len_v2v2(vert_p_co, mval_f);
if (len < 35) {
best.ele = (BMElem *)eve_test;
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 870996ddefa..0d568363b00 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -338,7 +338,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_CAGE,
},
mval_fl,
NULL,
@@ -352,7 +352,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
depsgraph,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_CAGE,
},
ray_start,
ray_normal,
@@ -387,7 +387,11 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
}
ED_gizmotypes_snap_3d_update(
- snap_gizmo, depsgraph, ruler_info->region, v3d, ruler_info->wm, mval_fl, co, NULL);
+ snap_gizmo, depsgraph, ruler_info->region, v3d, ruler_info->wm, mval_fl);
+
+ if (ED_gizmotypes_snap_3d_is_enabled(snap_gizmo)) {
+ ED_gizmotypes_snap_3d_data_get(snap_gizmo, co, NULL, NULL, NULL);
+ }
}
return true;
}
@@ -442,7 +446,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup)
gpl = view3d_ruler_layer_get(gpd);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false);
+ gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false, false);
copy_v4_v4(gpl->color, U.gpencil_new_layer_col);
gpl->thickness = 1;
gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER;
@@ -1120,12 +1124,13 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup)
const wmGizmoType *gzt_snap;
gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true);
gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL);
+
RNA_enum_set(gizmo->ptr,
"snap_elements_force",
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
/* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */
SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT));
-
+ ED_gizmotypes_snap_3d_flag_set(gizmo, ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE);
WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f});
wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_ruler_add", true);
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
index 3f258a0699a..ad91af73a71 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
@@ -143,7 +143,7 @@ static void WIDGETGROUP_tool_generic_refresh(const bContext *C, wmGizmoGroup *gz
const bool hide = ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.use_only_center = true,
- .orientation_type = orientation + 1,
+ .orientation_index = orientation + 1,
},
&tbounds) == 0;
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 118ec2425fc..6f07cb8b44d 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -122,17 +122,7 @@ void VIEW3D_OT_walk(struct wmOperatorType *ot);
void view3d_main_region_draw(const struct bContext *C, struct ARegion *region);
void view3d_draw_region_info(const struct bContext *C, struct ARegion *region);
-void ED_view3d_draw_depth(struct Depsgraph *depsgraph,
- struct ARegion *region,
- View3D *v3d,
- bool alphaoverride);
-
/* view3d_draw_legacy.c */
-void ED_view3d_draw_depth_gpencil(struct Depsgraph *depsgraph,
- Scene *scene,
- struct ARegion *region,
- View3D *v3d);
-
void ED_view3d_draw_select_loop(struct Depsgraph *depsgraph,
ViewContext *vc,
Scene *scene,
@@ -156,6 +146,7 @@ void VIEW3D_OT_select_circle(struct wmOperatorType *ot);
void VIEW3D_OT_select_box(struct wmOperatorType *ot);
void VIEW3D_OT_select_lasso(struct wmOperatorType *ot);
void VIEW3D_OT_select_menu(struct wmOperatorType *ot);
+void VIEW3D_OT_bone_select_menu(struct wmOperatorType *ot);
/* view3d_view.c */
void VIEW3D_OT_smoothview(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c
index 344168e895b..56dedbbdbb2 100644
--- a/source/blender/editors/space_view3d/view3d_ops.c
+++ b/source/blender/editors/space_view3d/view3d_ops.c
@@ -187,6 +187,7 @@ void view3d_operatortypes(void)
WM_operatortype_append(VIEW3D_OT_cursor3d);
WM_operatortype_append(VIEW3D_OT_select_lasso);
WM_operatortype_append(VIEW3D_OT_select_menu);
+ WM_operatortype_append(VIEW3D_OT_bone_select_menu);
WM_operatortype_append(VIEW3D_OT_camera_to_view);
WM_operatortype_append(VIEW3D_OT_camera_to_view_selected);
WM_operatortype_append(VIEW3D_OT_object_as_camera);
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index 48f274ca71b..e602521f6a2 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -252,7 +252,7 @@ static int dot_v3_array_find_max_index(const float dirs[][3],
}
/**
- * Re-order \a mat so \a axis_align uses it's own axis which is closest to \a v.
+ * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v.
*/
static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const float v[3])
{
@@ -323,7 +323,7 @@ static bool idp_poject_surface_normal(SnapObjectContext *snap_context,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
mval_fl,
NULL,
@@ -941,7 +941,7 @@ static void view3d_interactive_add_calc_plane(bContext *C,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
mval_fl,
NULL,
@@ -1058,9 +1058,7 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv
ipd->region,
ipd->v3d,
G_MAIN->wm.first,
- mval_fl,
- NULL,
- NULL);
+ mval_fl);
}
}
@@ -1474,10 +1472,27 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve
RNA_float_set_array(&op_props, "rotation", rotation);
RNA_float_set_array(&op_props, "location", location);
RNA_float_set_array(&op_props, "scale", scale);
- /* Always use default size here. */
+
+ /* Always use the defaults here since desired bounds have been set interactively, it does
+ * not make sense to use a different values from a previous command. */
if (ipd->primitive_type == PLACE_PRIMITIVE_TYPE_CUBE) {
RNA_float_set(&op_props, "size", 2.0f);
}
+ if (ELEM(ipd->primitive_type,
+ PLACE_PRIMITIVE_TYPE_CYLINDER,
+ PLACE_PRIMITIVE_TYPE_SPHERE_UV,
+ PLACE_PRIMITIVE_TYPE_SPHERE_ICO)) {
+ RNA_float_set(&op_props, "radius", 1.0f);
+ }
+ if (ELEM(
+ ipd->primitive_type, PLACE_PRIMITIVE_TYPE_CYLINDER, PLACE_PRIMITIVE_TYPE_CONE)) {
+ RNA_float_set(&op_props, "depth", 2.0f);
+ }
+ if (ipd->primitive_type == PLACE_PRIMITIVE_TYPE_CONE) {
+ RNA_float_set(&op_props, "radius1", 1.0f);
+ RNA_float_set(&op_props, "radius2", 0.0f);
+ }
+
WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
WM_operator_properties_free(&op_props);
}
@@ -1501,18 +1516,17 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve
ipd->is_snap_found = false;
if (ipd->use_snap) {
if (ipd->snap_gizmo != NULL) {
- ED_gizmotypes_snap_3d_toggle_set(ipd->snap_gizmo, ipd->use_snap);
+ ED_gizmotypes_snap_3d_flag_set(ipd->snap_gizmo, ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE);
if (ED_gizmotypes_snap_3d_update(ipd->snap_gizmo,
CTX_data_ensure_evaluated_depsgraph(C),
ipd->region,
ipd->v3d,
G_MAIN->wm.first,
- mval_fl,
- ipd->snap_co,
- NULL)) {
+ mval_fl)) {
+ ED_gizmotypes_snap_3d_data_get(ipd->snap_gizmo, ipd->snap_co, NULL, NULL, NULL);
ipd->is_snap_found = true;
}
- ED_gizmotypes_snap_3d_toggle_clear(ipd->snap_gizmo);
+ ED_gizmotypes_snap_3d_flag_clear(ipd->snap_gizmo, ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE);
}
}
@@ -1748,7 +1762,7 @@ static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup
gizmo->flag |= WM_GIZMO_HIDDEN_KEYMAP;
}
- /* Sets the gizmos custom-data which has it's own free callback. */
+ /* Sets the gizmos custom-data which has its own free callback. */
preview_plane_cursor_setup(gzgroup);
}
@@ -2047,7 +2061,7 @@ static void cursor_plane_draw(bContext *C, int x, int y, void *customdata)
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
- const float scale_mod = U.gizmo_size * 2 * U.dpi_fac;
+ const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize;
float final_scale = (scale_mod * pixel_size);
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index 24d34e514c5..7547f8ee434 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -809,23 +809,30 @@ void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d,
/**
* Convert between region relative coordinates (x,y) and depth component z and
* a point in world space. */
-void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3])
+void ED_view3d_project_v3(const struct ARegion *region, const float world[3], float r_region_co[3])
{
/* Viewport is set up to make coordinates relative to the region, not window. */
RegionView3D *rv3d = region->regiondata;
const int viewport[4] = {0, 0, region->winx, region->winy};
+ GPU_matrix_project_3fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
+}
- GPU_matrix_project(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
+void ED_view3d_project_v2(const struct ARegion *region, const float world[3], float r_region_co[2])
+{
+ /* Viewport is set up to make coordinates relative to the region, not window. */
+ RegionView3D *rv3d = region->regiondata;
+ const int viewport[4] = {0, 0, region->winx, region->winy};
+ GPU_matrix_project_2fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
}
-bool ED_view3d_unproject(
+bool ED_view3d_unproject_v3(
const struct ARegion *region, float regionx, float regiony, float regionz, float world[3])
{
RegionView3D *rv3d = region->regiondata;
const int viewport[4] = {0, 0, region->winx, region->winy};
const float region_co[3] = {regionx, regiony, regionz};
- return GPU_matrix_unproject(region_co, rv3d->viewmat, rv3d->winmat, viewport, world);
+ return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world);
}
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 3166b818d3c..757ed13ac28 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -96,6 +96,7 @@
#include "ED_select_utils.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "GPU_matrix.h"
@@ -1432,6 +1433,8 @@ void VIEW3D_OT_select_lasso(wmOperatorType *ot)
typedef struct SelMenuItemF {
char idname[MAX_ID_NAME - 2];
int icon;
+ Base *base_ptr;
+ void *item_ptr;
} SelMenuItemF;
#define SEL_MENU_SIZE 22
@@ -1580,7 +1583,7 @@ static Base *object_mouse_select_menu(bContext *C,
{
short baseCount = 0;
bool ok;
- LinkNode *linklist = NULL;
+ LinkNodePair linklist = {NULL, NULL};
/* handle base->object->select_id */
CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
@@ -1608,7 +1611,7 @@ static Base *object_mouse_select_menu(bContext *C,
if (ok) {
baseCount++;
- BLI_linklist_prepend(&linklist, base);
+ BLI_linklist_append(&linklist, base);
if (baseCount == SEL_MENU_SIZE) {
break;
@@ -1621,8 +1624,8 @@ static Base *object_mouse_select_menu(bContext *C,
return NULL;
}
if (baseCount == 1) {
- Base *base = (Base *)linklist->link;
- BLI_linklist_free(linklist, NULL);
+ Base *base = (Base *)linklist.list->link;
+ BLI_linklist_free(linklist.list, NULL);
return base;
}
@@ -1632,7 +1635,7 @@ static Base *object_mouse_select_menu(bContext *C,
memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
- for (node = linklist, i = 0; node; node = node->next, i++) {
+ for (node = linklist.list, i = 0; node; node = node->next, i++) {
Base *base = node->link;
Object *ob = base->object;
const char *name = ob->id.name + 2;
@@ -1651,10 +1654,231 @@ static Base *object_mouse_select_menu(bContext *C,
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
WM_operator_properties_free(&ptr);
- BLI_linklist_free(linklist, NULL);
+ BLI_linklist_free(linklist.list, NULL);
return NULL;
}
+static int bone_select_menu_exec(bContext *C, wmOperator *op)
+{
+ const int name_index = RNA_enum_get(op->ptr, "name");
+ const bool extend = RNA_boolean_get(op->ptr, "extend");
+ const bool deselect = RNA_boolean_get(op->ptr, "deselect");
+ const bool toggle = RNA_boolean_get(op->ptr, "toggle");
+
+ View3D *v3d = CTX_wm_view3d(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ const Base *oldbasact = BASACT(view_layer);
+
+ Base *basact = object_mouse_select_menu_data[name_index].base_ptr;
+
+ if (basact == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BLI_assert(BASE_SELECTABLE(v3d, basact));
+
+ if (basact->object->mode == OB_MODE_EDIT) {
+ EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr;
+ ED_armature_edit_select_pick_bone(C, basact, ebone, BONE_SELECTED, extend, deselect, toggle);
+ }
+ else {
+ bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr;
+ ED_armature_pose_select_pick_bone(
+ view_layer, v3d, basact->object, pchan->bone, extend, deselect, toggle);
+ }
+
+ /* Weak but ensures we activate the menu again before using the enum. */
+ memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
+
+ /* We make the armature selected:
+ * Not-selected active object in posemode won't work well for tools. */
+ ED_object_base_select(basact, BA_SELECT);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object);
+
+ /* In weight-paint, we use selected bone to select vertex-group,
+ * so don't switch to new active object. */
+ if (oldbasact && (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT)) {
+ /* Prevent activating.
+ * Selection causes this to be considered the 'active' pose in weight-paint mode.
+ * Eventually this limitation may be removed.
+ * For now, de-select all other pose objects deforming this mesh. */
+ ED_armature_pose_select_in_wpaint_mode(view_layer, basact);
+
+ basact = NULL;
+ }
+
+ /* Undo? */
+ Scene *scene = CTX_data_scene(C);
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+
+ ED_outliner_select_sync_from_object_tag(C);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Select Menu";
+ ot->description = "Menu bone selection";
+ ot->idname = "VIEW3D_OT_bone_select_menu";
+
+ /* api callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = bone_select_menu_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* keyingset to use (dynamic enum) */
+ prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Bone Name", "");
+ RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
+ ot->prop = prop;
+
+ RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
+ RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
+ RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+}
+static bool bone_mouse_select_menu(bContext *C,
+ const uint *buffer,
+ const int hits,
+ const bool is_editmode,
+ const bool extend,
+ const bool deselect,
+ const bool toggle)
+{
+ BLI_assert(buffer);
+
+ short baseCount = 0;
+ LinkNodePair base_list = {NULL, NULL};
+ LinkNodePair bone_list = {NULL, NULL};
+ GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu");
+
+ /* Select logic taken from ed_armature_pick_bone_from_selectbuffer_impl in armature_select.c */
+ for (int a = 0; a < hits; a++) {
+ void *bone_ptr = NULL;
+ Base *bone_base = NULL;
+ uint hitresult = buffer[3 + (a * 4)];
+
+ if (!(hitresult & BONESEL_ANY)) {
+ /* To avoid including objects in selection. */
+ continue;
+ }
+
+ hitresult &= ~BONESEL_ANY;
+ const uint hit_object = hitresult & 0xFFFF;
+
+ /* Find the hit bone base (armature object). */
+ CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
+ if (base->object->runtime.select_id == hit_object) {
+ bone_base = base;
+ break;
+ }
+ }
+ CTX_DATA_END;
+
+ if (!bone_base) {
+ continue;
+ }
+
+ /* Determine what the current bone is */
+ if (is_editmode) {
+ EditBone *ebone;
+ const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
+ bArmature *arm = bone_base->object->data;
+ ebone = BLI_findlink(arm->edbo, hit_bone);
+ if (ebone && !(ebone->flag & BONE_UNSELECTABLE)) {
+ bone_ptr = ebone;
+ }
+ }
+ else {
+ bPoseChannel *pchan;
+ const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
+ pchan = BLI_findlink(&bone_base->object->pose->chanbase, hit_bone);
+ if (pchan && !(pchan->bone->flag & BONE_UNSELECTABLE)) {
+ bone_ptr = pchan;
+ }
+ }
+
+ if (!bone_ptr) {
+ continue;
+ }
+ /* We can hit a bone multiple times, so make sure we are not adding an already included bone
+ * to the list.*/
+ const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr);
+
+ if (!is_duplicate_bone) {
+ baseCount++;
+ BLI_linklist_append(&base_list, bone_base);
+ BLI_linklist_append(&bone_list, bone_ptr);
+ BLI_gset_insert(added_bones, bone_ptr);
+
+ if (baseCount == SEL_MENU_SIZE) {
+ break;
+ }
+ }
+ }
+
+ BLI_gset_free(added_bones, NULL);
+
+ if (baseCount == 0) {
+ return false;
+ }
+ if (baseCount == 1) {
+ BLI_linklist_free(base_list.list, NULL);
+ BLI_linklist_free(bone_list.list, NULL);
+ return false;
+ }
+
+ /* UI, full in static array values that we later use in an enum function */
+ LinkNode *bone_node, *base_node;
+ int i;
+
+ memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
+
+ for (base_node = base_list.list, bone_node = bone_list.list, i = 0; bone_node;
+ base_node = base_node->next, bone_node = bone_node->next, i++) {
+ char *name;
+
+ object_mouse_select_menu_data[i].base_ptr = base_node->link;
+
+ if (is_editmode) {
+ EditBone *ebone = bone_node->link;
+ object_mouse_select_menu_data[i].item_ptr = ebone;
+ name = ebone->name;
+ }
+ else {
+ bPoseChannel *pchan = bone_node->link;
+ object_mouse_select_menu_data[i].item_ptr = pchan;
+ name = pchan->name;
+ }
+
+ BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2);
+ object_mouse_select_menu_data[i].icon = ICON_BONE_DATA;
+ }
+
+ wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_bone_select_menu", false);
+ PointerRNA ptr;
+
+ WM_operator_properties_create_ptr(&ptr, ot);
+ RNA_boolean_set(&ptr, "extend", extend);
+ RNA_boolean_set(&ptr, "deselect", deselect);
+ RNA_boolean_set(&ptr, "toggle", toggle);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
+ WM_operator_properties_free(&ptr);
+
+ BLI_linklist_free(base_list.list, NULL);
+ BLI_linklist_free(bone_list.list, NULL);
+ return true;
+}
+
static bool selectbuffer_has_bones(const uint *buffer, const uint hits)
{
for (uint i = 0; i < hits; i++) {
@@ -2113,7 +2337,13 @@ static bool ed_object_select_pick(bContext *C,
/* note; shift+alt goes to group-flush-selecting */
if (enumerate) {
- basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle);
+ if (has_bones &&
+ bone_mouse_select_menu(C, buffer, hits, false, extend, deselect, toggle)) {
+ basact = NULL;
+ }
+ else {
+ basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle);
+ }
}
else {
basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest);
@@ -2410,7 +2640,20 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
}
}
else if (obedit->type == OB_ARMATURE) {
- retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
+ if (enumerate) {
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewContext vc;
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+
+ uint buffer[MAXPICKBUF];
+ const int hits = mixed_bones_object_selectbuffer(
+ &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true);
+ retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle);
+ }
+ if (!retval) {
+ retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
+ }
+
if (!retval && deselect_all) {
retval = ED_armature_edit_deselect_all_visible_multi(C);
}
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index e3acda9bffb..8ae5d4a29e9 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -36,6 +36,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_array_utils.h"
#include "BLI_bitmap_draw_2d.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
@@ -298,8 +299,8 @@ void ED_view3d_clipping_calc(
float xs = (ELEM(val, 0, 3)) ? rect->xmin : rect->xmax;
float ys = (ELEM(val, 0, 1)) ? rect->ymin : rect->ymax;
- ED_view3d_unproject(region, xs, ys, 0.0, bb->vec[val]);
- ED_view3d_unproject(region, xs, ys, 1.0, bb->vec[4 + val]);
+ ED_view3d_unproject_v3(region, xs, ys, 0.0, bb->vec[val]);
+ ED_view3d_unproject_v3(region, xs, ys, 1.0, bb->vec[4 + val]);
}
/* optionally transform to object space */
@@ -1024,6 +1025,7 @@ static float view_autodist_depth_margin(ARegion *region, const int mval[2], int
/**
* Get the world-space 3d location from a screen-space 2d point.
+ * TODO: Implement #alphaoverride. We don't want to zoom into billboards.
*
* \param mval: Input screen-space pixel location.
* \param mouse_worldloc: Output world-space location.
@@ -1034,7 +1036,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
View3D *v3d,
const int mval[2],
float mouse_worldloc[3],
- const bool alphaoverride,
+ const bool UNUSED(alphaoverride),
const float fallback_depth_pt[3])
{
float depth_close;
@@ -1042,7 +1044,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
bool depth_ok = false;
/* Get Z Depths, needed for perspective, nice for ortho */
- ED_view3d_draw_depth(depsgraph, region, v3d, alphaoverride);
+ ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
/* Attempt with low margin's first */
int i = 0;
@@ -1055,7 +1057,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
float centx = (float)mval[0] + 0.5f;
float centy = (float)mval[1] + 0.5f;
- if (ED_view3d_unproject(region, centx, centy, depth_close, mouse_worldloc)) {
+ if (ED_view3d_unproject_v3(region, centx, centy, depth_close, mouse_worldloc)) {
return true;
}
}
@@ -1067,22 +1069,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
return false;
}
-void ED_view3d_autodist_init(Depsgraph *depsgraph, ARegion *region, View3D *v3d, int mode)
-{
- /* Get Z Depths, needed for perspective, nice for ortho */
- switch (mode) {
- case 0:
- ED_view3d_draw_depth(depsgraph, region, v3d, true);
- break;
- case 1: {
- Scene *scene = DEG_get_evaluated_scene(depsgraph);
- ED_view3d_draw_depth_gpencil(depsgraph, scene, region, v3d);
- break;
- }
- }
-}
-
-/* no 4x4 sampling, run #ED_view3d_autodist_init first */
+/* no 4x4 sampling, run #ED_view3d_depth_override first */
bool ED_view3d_autodist_simple(ARegion *region,
const int mval[2],
float mouse_worldloc[3],
@@ -1104,7 +1091,7 @@ bool ED_view3d_autodist_simple(ARegion *region,
float centx = (float)mval[0] + 0.5f;
float centy = (float)mval[1] + 0.5f;
- return ED_view3d_unproject(region, centx, centy, depth, mouse_worldloc);
+ return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc);
}
bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth)
@@ -1643,19 +1630,68 @@ bool ED_view3d_camera_to_view_selected(struct Main *bmain,
/** \name Depth Buffer Utilities
* \{ */
-float ED_view3d_depth_read_cached(const ViewContext *vc, const int mval[2])
+struct ReadData {
+ int count;
+ int count_max;
+ float r_depth;
+};
+
+static bool depth_read_test_fn(const void *value, void *userdata)
+{
+ struct ReadData *data = userdata;
+ float depth = *(float *)value;
+ if (depth < data->r_depth) {
+ data->r_depth = depth;
+ }
+
+ if ((++data->count) >= data->count_max) {
+ /* Outside the margin. */
+ return true;
+ }
+ return false;
+}
+
+bool ED_view3d_depth_read_cached(const ViewDepths *vd,
+ const int mval[2],
+ int margin,
+ float *r_depth)
{
- ViewDepths *vd = vc->rv3d->depths;
+ if (!vd || !vd->depths) {
+ return false;
+ }
int x = mval[0];
int y = mval[1];
+ if (x < 0 || y < 0 || x >= vd->w || y >= vd->h) {
+ return false;
+ }
+
+ float depth = 1.0f;
+ if (margin) {
+ int shape[2] = {vd->w, vd->h};
+ int pixel_count = (min_ii(x + margin + 1, shape[1]) - max_ii(x - margin, 0)) *
+ (min_ii(y + margin + 1, shape[0]) - max_ii(y - margin, 0));
+
+ struct ReadData data;
+ data.count = 0;
+ data.count_max = pixel_count;
+ data.r_depth = 1.0f;
- if (vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h) {
- return vd->depths[y * vd->w + x];
+ /* TODO: No need to go spiral. */
+ BLI_array_iter_spiral_square(vd->depths, shape, mval, depth_read_test_fn, &data);
+ depth = data.r_depth;
+ }
+ else {
+ depth = vd->depths[y * vd->w + x];
}
BLI_assert(1.0 <= vd->depth_range[1]);
- return 1.0f;
+ if (depth != 1.0f) {
+ *r_depth = depth;
+ return true;
+ }
+
+ return false;
}
bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
@@ -1676,9 +1712,11 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
for (int y = 0; y < 2; y++) {
const int mval_ofs[2] = {mval[0] + (x - 1), mval[1] + (y - 1)};
- const double depth = (double)ED_view3d_depth_read_cached(vc, mval_ofs);
+ float depth_fl = 1.0f;
+ ED_view3d_depth_read_cached(depths, mval_ofs, 0, &depth_fl);
+ const double depth = (double)depth_fl;
if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
- if (ED_view3d_depth_unproject(region, mval_ofs, depth, coords[i])) {
+ if (ED_view3d_depth_unproject_v3(region, mval_ofs, depth, coords[i])) {
depths_valid[i] = true;
}
}
@@ -1713,14 +1751,14 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
return false;
}
-bool ED_view3d_depth_unproject(const ARegion *region,
- const int mval[2],
- const double depth,
- float r_location_world[3])
+bool ED_view3d_depth_unproject_v3(const ARegion *region,
+ const int mval[2],
+ const double depth,
+ float r_location_world[3])
{
float centx = (float)mval[0] + 0.5f;
float centy = (float)mval[1] + 0.5f;
- return ED_view3d_unproject(region, centx, centy, depth, r_location_world);
+ return ED_view3d_unproject_v3(region, centx, centy, depth, r_location_world);
}
void ED_view3d_depth_tag_update(RegionView3D *rv3d)
diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c
index cbd65e3175d..ab4cf0c2135 100644
--- a/source/blender/editors/space_view3d/view3d_walk.c
+++ b/source/blender/editors/space_view3d/view3d_walk.c
@@ -407,7 +407,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
/* Avoid having to convert the edit-mesh to a regular mesh. */
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
ray_start,
ray_normal,
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 2fbcbe22349..17bfc23aea7 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -74,7 +74,7 @@
static void drawTransformApply(const struct bContext *C, ARegion *region, void *arg);
-static void initSnapSpatial(TransInfo *t, float r_snap[3]);
+static void initSnapSpatial(TransInfo *t, float r_snap[2]);
bool transdata_check_local_islands(TransInfo *t, short around)
{
@@ -724,81 +724,92 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
static bool transform_event_modal_constraint(TransInfo *t, short modal_type)
{
- if (!(t->flag & T_NO_CONSTRAINT)) {
- if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) {
+ if (t->flag & T_NO_CONSTRAINT) {
+ return false;
+ }
+
+ if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) {
+ return false;
+ }
+
+ int constraint_curr = -1;
+
+ if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) {
+ t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE);
+
+ /* Avoid changing orientation in this case. */
+ constraint_curr = -2;
+ }
+ else if (t->con.mode & CON_APPLY) {
+ constraint_curr = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
+ }
+
+ int constraint_new;
+ const char *msg_2d = "", *msg_3d = "";
+
+ /* Initialize */
+ switch (modal_type) {
+ case TFM_MODAL_AXIS_X:
+ msg_2d = TIP_("along X");
+ msg_3d = TIP_("along %s X");
+ constraint_new = CON_AXIS0;
+ break;
+ case TFM_MODAL_AXIS_Y:
+ msg_2d = TIP_("along Y");
+ msg_3d = TIP_("along %s Y");
+ constraint_new = CON_AXIS1;
+ break;
+ case TFM_MODAL_AXIS_Z:
+ msg_2d = TIP_("along Z");
+ msg_3d = TIP_("along %s Z");
+ constraint_new = CON_AXIS2;
+ break;
+ case TFM_MODAL_PLANE_X:
+ msg_3d = TIP_("locking %s X");
+ constraint_new = CON_AXIS1 | CON_AXIS2;
+ break;
+ case TFM_MODAL_PLANE_Y:
+ msg_3d = TIP_("locking %s Y");
+ constraint_new = CON_AXIS0 | CON_AXIS2;
+ break;
+ case TFM_MODAL_PLANE_Z:
+ msg_3d = TIP_("locking %s Z");
+ constraint_new = CON_AXIS0 | CON_AXIS1;
+ break;
+ default:
+ /* Invalid key */
return false;
- }
- int constraint_curr = (t->con.mode & CON_APPLY) ?
- t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2) :
- -1;
- int constraint_new;
- const char *msg_2d = "", *msg_3d = "";
+ }
- /* Initialize */
- switch (modal_type) {
- case TFM_MODAL_AXIS_X:
- msg_2d = TIP_("along X");
- msg_3d = TIP_("along %s X");
- constraint_new = CON_AXIS0;
- break;
- case TFM_MODAL_AXIS_Y:
- msg_2d = TIP_("along Y");
- msg_3d = TIP_("along %s Y");
- constraint_new = CON_AXIS1;
- break;
- case TFM_MODAL_AXIS_Z:
- msg_2d = TIP_("along Z");
- msg_3d = TIP_("along %s Z");
- constraint_new = CON_AXIS2;
- break;
- case TFM_MODAL_PLANE_X:
- msg_3d = TIP_("locking %s X");
- constraint_new = CON_AXIS1 | CON_AXIS2;
- break;
- case TFM_MODAL_PLANE_Y:
- msg_3d = TIP_("locking %s Y");
- constraint_new = CON_AXIS0 | CON_AXIS2;
- break;
- case TFM_MODAL_PLANE_Z:
- msg_3d = TIP_("locking %s Z");
- constraint_new = CON_AXIS0 | CON_AXIS1;
- break;
- default:
- /* Invalid key */
- return false;
+ if (t->flag & T_2D_EDIT) {
+ BLI_assert(modal_type < TFM_MODAL_PLANE_X);
+ if (constraint_new == CON_AXIS2) {
+ return false;
}
-
- if (t->flag & T_2D_EDIT) {
- BLI_assert(modal_type < TFM_MODAL_PLANE_X);
- if (constraint_new == CON_AXIS2) {
- return false;
- }
- if (constraint_curr == constraint_new) {
- stopConstraint(t);
- }
- else {
- setUserConstraint(t, constraint_new, msg_2d);
- }
+ if (constraint_curr == constraint_new) {
+ stopConstraint(t);
}
else {
- short orient_index = 1;
- if (t->orient_curr == 0 || ELEM(constraint_curr, -1, constraint_new)) {
- /* Successive presses on existing axis, cycle orientation modes. */
- orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient));
- }
+ setUserConstraint(t, constraint_new, msg_2d);
+ }
+ }
+ else {
+ short orient_index = 1;
+ if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) {
+ /* Successive presses on existing axis, cycle orientation modes. */
+ orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient));
+ }
- transform_orientations_current_set(t, orient_index);
- if (orient_index == 0) {
- stopConstraint(t);
- }
- else {
- setUserConstraint(t, constraint_new, msg_3d);
- }
+ transform_orientations_current_set(t, orient_index);
+ if (orient_index == 0) {
+ stopConstraint(t);
+ }
+ else {
+ setUserConstraint(t, constraint_new, msg_3d);
}
- t->redraw |= TREDRAW_HARD;
- return true;
}
- return false;
+ t->redraw |= TREDRAW_HARD;
+ return true;
}
int transformEvent(TransInfo *t, const wmEvent *event)
@@ -814,7 +825,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
else if (event->type == MOUSEMOVE) {
- if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) {
+ if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) {
t->con.mode |= CON_SELECT;
}
@@ -1062,10 +1073,10 @@ int transformEvent(TransInfo *t, const wmEvent *event)
t->state = TRANS_CONFIRM;
}
else if ((t->flag & T_NO_CONSTRAINT) == 0) {
- if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) {
+ if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) {
/* Confirm. */
postSelectConstraint(t);
- t->modifiers &= ~(MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE);
+ t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE);
}
else {
if (t->options & CTX_CAMERA) {
@@ -1079,8 +1090,9 @@ int transformEvent(TransInfo *t, const wmEvent *event)
}
}
else {
- t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ? MOD_CONSTRAINT_SELECT :
- MOD_CONSTRAINT_PLANE;
+ t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ?
+ MOD_CONSTRAINT_SELECT_AXIS :
+ MOD_CONSTRAINT_SELECT_PLANE;
if (t->con.mode & CON_APPLY) {
stopConstraint(t);
}
@@ -1412,25 +1424,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
ToolSettings *ts = CTX_data_tool_settings(C);
PropertyRNA *prop;
- if (!(t->con.mode & CON_APPLY) && (t->flag & T_MODAL) &&
- ELEM(t->mode, TFM_TRANSLATION, TFM_RESIZE)) {
- /* When redoing these modes the first time, it's more convenient to save
- * in the Global orientation. */
- if (t->mode == TFM_TRANSLATION) {
- mul_m3_v3(t->spacemtx, t->values_final);
- }
- else {
- float tmat[3][3], sizemat[3][3];
- size_to_mat3(sizemat, t->values_final);
- mul_m3_m3m3(tmat, t->spacemtx, sizemat);
- mat3_to_size(t->values_final, tmat);
- }
-
- BLI_assert(t->orient_curr == 0);
- unit_m3(t->spacemtx);
- t->orient[0].type = V3D_ORIENT_GLOBAL;
- }
-
/* Save back mode in case we're in the generic operator */
if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
RNA_property_enum_set(op->ptr, prop, t->mode);
@@ -1638,7 +1631,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2])
/**
* \note caller needs to free 't' on a 0 return
- * \warning \a event might be NULL (when tweaking from redo panel)
+ * \warning \a event might be NULL (when tweaking from redo panel)
* \see #saveTransform which writes these values back.
*/
bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index f2d5800abb7..f0ced665679 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -46,6 +46,7 @@
* \{ */
struct ARegion;
+struct BMPartialUpdate;
struct Depsgraph;
struct NumInput;
struct Object;
@@ -88,12 +89,11 @@ typedef enum {
CTX_TEXTURE_SPACE = (1 << 9),
CTX_NO_PET = (1 << 10),
- CTX_NO_MIRROR = (1 << 11),
- CTX_AUTOCONFIRM = (1 << 12),
+ CTX_AUTOCONFIRM = (1 << 11),
/** When transforming object's, adjust the object data so it stays in the same place. */
- CTX_OBMODE_XFORM_OBDATA = (1 << 13),
+ CTX_OBMODE_XFORM_OBDATA = (1 << 12),
/** Transform object parents without moving their children. */
- CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14),
+ CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13),
} eTContext;
/** #TransInfo.flag */
@@ -105,60 +105,59 @@ typedef enum {
/** restrictions flags */
T_NO_CONSTRAINT = 1 << 2,
T_NULL_ONE = 1 << 3,
- T_NO_ZERO = 1 << 4,
- T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE | T_NO_ZERO,
+ T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE,
- T_PROP_EDIT = 1 << 5,
- T_PROP_CONNECTED = 1 << 6,
- T_PROP_PROJECTED = 1 << 7,
+ T_PROP_EDIT = 1 << 4,
+ T_PROP_CONNECTED = 1 << 5,
+ T_PROP_PROJECTED = 1 << 6,
T_PROP_EDIT_ALL = T_PROP_EDIT | T_PROP_CONNECTED | T_PROP_PROJECTED,
- T_V3D_ALIGN = 1 << 8,
+ T_V3D_ALIGN = 1 << 7,
/** For 2D views such as UV or f-curve. */
- T_2D_EDIT = 1 << 9,
- T_CLIP_UV = 1 << 10,
+ T_2D_EDIT = 1 << 8,
+ T_CLIP_UV = 1 << 9,
/** Auto-IK is on. */
- T_AUTOIK = 1 << 11,
+ T_AUTOIK = 1 << 10,
/** Don't use mirror even if the data-block option is set. */
- T_NO_MIRROR = 1 << 12,
+ T_NO_MIRROR = 1 << 11,
/** To indicate that the value set in the `value` parameter is the final
* value of the transformation, modified only by the constrain. */
- T_INPUT_IS_VALUES_FINAL = 1 << 13,
+ T_INPUT_IS_VALUES_FINAL = 1 << 12,
/** To specify if we save back settings at the end. */
- T_MODAL = 1 << 14,
+ T_MODAL = 1 << 13,
/** No re-topology (projection). */
- T_NO_PROJECT = 1 << 15,
+ T_NO_PROJECT = 1 << 14,
- T_RELEASE_CONFIRM = 1 << 16,
+ T_RELEASE_CONFIRM = 1 << 15,
/** Alternative transformation. used to add offset to tracking markers. */
- T_ALT_TRANSFORM = 1 << 17,
+ T_ALT_TRANSFORM = 1 << 16,
/** #TransInfo.center has been set, don't change it. */
- T_OVERRIDE_CENTER = 1 << 18,
+ T_OVERRIDE_CENTER = 1 << 17,
- T_MODAL_CURSOR_SET = 1 << 19,
+ T_MODAL_CURSOR_SET = 1 << 18,
- T_CLNOR_REBUILD = 1 << 20,
+ T_CLNOR_REBUILD = 1 << 19,
/** Merges unselected into selected after transforming (runs after transforming). */
- T_AUTOMERGE = 1 << 21,
+ T_AUTOMERGE = 1 << 20,
/** Runs auto-merge & splits. */
- T_AUTOSPLIT = 1 << 22,
+ T_AUTOSPLIT = 1 << 21,
} eTFlag;
/** #TransInfo.modifiers */
typedef enum {
- MOD_CONSTRAINT_SELECT = 1 << 0,
+ MOD_CONSTRAINT_SELECT_AXIS = 1 << 0,
MOD_PRECISION = 1 << 1,
MOD_SNAP = 1 << 2,
MOD_SNAP_INVERT = 1 << 3,
- MOD_CONSTRAINT_PLANE = 1 << 4,
+ MOD_CONSTRAINT_SELECT_PLANE = 1 << 4,
} eTModifier;
/** #TransSnap.status */
@@ -432,14 +431,14 @@ typedef struct TransCustomDataContainer {
/**
* Container for Transform Data
*
- * Used to implement multi-object modes, so each object can have it's
+ * Used to implement multi-object modes, so each object can have its
* own data array as well as object matrix, local center etc.
*
* Anything that can't be shared between all objects
* and doesn't make sense to store for every vertex (in the #TransDataContainer.data).
*
* \note at some point this could be used to store non object containers
- * although this only makes sense if each container has it's own matrices,
+ * although this only makes sense if each container has its own matrices,
* otherwise all elements may as well be stored in one array (#TransDataContainer.data),
* as is already done for curve-objects, f-curves. etc.
*/
@@ -598,11 +597,18 @@ typedef struct TransInfo {
* mouse button then.) */
bool is_launch_event_tweak;
+ bool is_orient_set;
+
struct {
short type;
float matrix[3][3];
} orient[3];
- short orient_curr;
+
+ enum {
+ O_DEFAULT = 0,
+ O_SCENE,
+ O_SET,
+ } orient_curr;
/** backup from view3d, to restore on end. */
short gizmo_flag;
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 2037981e655..8c74d5349ba 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -953,9 +953,8 @@ void startConstraint(TransInfo *t)
void stopConstraint(TransInfo *t)
{
- if (t->orient_curr != 0) {
- t->orient_curr = 0;
- transform_orientations_current_set(t, t->orient_curr);
+ if (t->orient_curr != O_DEFAULT) {
+ transform_orientations_current_set(t, O_DEFAULT);
}
t->con.mode &= ~(CON_APPLY | CON_SELECT);
@@ -971,12 +970,11 @@ void stopConstraint(TransInfo *t)
void initSelectConstraint(TransInfo *t)
{
- if (t->orient_curr == 0) {
- transform_orientations_current_set(t, 1);
+ if (t->orient_curr == O_DEFAULT) {
+ transform_orientations_current_set(t, O_SCENE);
}
setUserConstraint(t, CON_APPLY | CON_SELECT, "%s");
- setNearestAxis(t);
}
void selectConstraint(TransInfo *t)
@@ -1063,7 +1061,7 @@ static void setNearestAxis3d(TransInfo *t)
}
if (len[0] <= len[1] && len[0] <= len[2]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
+ if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) {
t->con.mode |= (CON_AXIS1 | CON_AXIS2);
BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename);
}
@@ -1073,7 +1071,7 @@ static void setNearestAxis3d(TransInfo *t)
}
}
else if (len[1] <= len[0] && len[1] <= len[2]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
+ if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) {
t->con.mode |= (CON_AXIS0 | CON_AXIS2);
BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename);
}
@@ -1083,7 +1081,7 @@ static void setNearestAxis3d(TransInfo *t)
}
}
else if (len[2] <= len[1] && len[2] <= len[0]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
+ if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) {
t->con.mode |= (CON_AXIS0 | CON_AXIS1);
BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename);
}
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index c021c084a23..9e285dd2d26 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -37,6 +37,7 @@
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -505,9 +506,27 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
bool clipx = true, clipy = true;
float min[2], max[2];
- min[0] = min[1] = 0.0f;
- max[0] = t->aspect[0];
- max[1] = t->aspect[1];
+ /* Check if the current image in UV editor is a tiled image or not. */
+ const SpaceImage *sima = t->area->spacedata.first;
+ const Image *image = sima->image;
+ const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
+ /* Stores the coordinates of the closest UDIM tile.
+ * Also acts as an offset to the tile from the origin of UV space. */
+ float base_offset[2] = {0.0f, 0.0f};
+
+ /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */
+ if (is_tiled_image) {
+ int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global);
+ if (nearest_tile_index != -1) {
+ nearest_tile_index -= 1001;
+ /* Getting coordinates of nearest tile from the tile index. */
+ base_offset[0] = nearest_tile_index % 10;
+ base_offset[1] = nearest_tile_index / 10;
+ }
+ }
+
+ min[0] = min[1] = FLT_MAX;
+ max[0] = max[1] = FLT_MIN;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -520,42 +539,48 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
}
if (resize) {
- if (min[0] < 0.0f && t->center_global[0] > 0.0f && t->center_global[0] < t->aspect[0] * 0.5f) {
- vec[0] *= t->center_global[0] / (t->center_global[0] - min[0]);
+ if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] &&
+ t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) {
+ vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]);
}
- else if (max[0] > t->aspect[0] && t->center_global[0] < t->aspect[0]) {
- vec[0] *= (t->center_global[0] - t->aspect[0]) / (t->center_global[0] - max[0]);
+ else if (max[0] > (base_offset[0] + t->aspect[0]) &&
+ t->center_global[0] < (base_offset[0] + t->aspect[0])) {
+ vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) /
+ (t->center_global[0] - max[0]);
}
else {
clipx = 0;
}
- if (min[1] < 0.0f && t->center_global[1] > 0.0f && t->center_global[1] < t->aspect[1] * 0.5f) {
- vec[1] *= t->center_global[1] / (t->center_global[1] - min[1]);
+ if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] &&
+ t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) {
+ vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]);
}
- else if (max[1] > t->aspect[1] && t->center_global[1] < t->aspect[1]) {
- vec[1] *= (t->center_global[1] - t->aspect[1]) / (t->center_global[1] - max[1]);
+ else if (max[1] > (base_offset[1] + t->aspect[1]) &&
+ t->center_global[1] < (base_offset[1] + t->aspect[1])) {
+ vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) /
+ (t->center_global[1] - max[1]);
}
else {
clipy = 0;
}
}
else {
- if (min[0] < 0.0f) {
- vec[0] -= min[0];
+ if (min[0] < base_offset[0]) {
+ vec[0] += base_offset[0] - min[0];
}
- else if (max[0] > t->aspect[0]) {
- vec[0] -= max[0] - t->aspect[0];
+ else if (max[0] > base_offset[0] + t->aspect[0]) {
+ vec[0] -= max[0] - base_offset[0] - t->aspect[0];
}
else {
clipx = 0;
}
- if (min[1] < 0.0f) {
- vec[1] -= min[1];
+ if (min[1] < base_offset[1]) {
+ vec[1] += base_offset[1] - min[1];
}
- else if (max[1] > t->aspect[1]) {
- vec[1] -= max[1] - t->aspect[1];
+ else if (max[1] > base_offset[1] + t->aspect[1]) {
+ vec[1] -= max[1] - base_offset[1] - t->aspect[1];
}
else {
clipy = 0;
@@ -1122,8 +1147,7 @@ static void init_TransDataContainers(TransInfo *t,
for (int i = 0; i < objects_len; i++) {
TransDataContainer *tc = &t->data_container[i];
- if (((t->flag & T_NO_MIRROR) == 0) && ((t->options & CTX_NO_MIRROR) == 0) &&
- (objects[i]->type == OB_MESH)) {
+ if (!(t->flag & T_NO_MIRROR) && (objects[i]->type == OB_MESH)) {
tc->use_mirror_axis_x = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_X) != 0;
tc->use_mirror_axis_y = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Y) != 0;
tc->use_mirror_axis_z = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Z) != 0;
@@ -1472,91 +1496,89 @@ void createTransData(bContext *C, TransInfo *t)
/** \name Transform Data Recalc/Flush
* \{ */
-void clipMirrorModifier(TransInfo *t)
+void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc)
{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- Object *ob = tc->obedit;
- ModifierData *md = ob->modifiers.first;
- float tolerance[3] = {0.0f, 0.0f, 0.0f};
- int axis = 0;
-
- for (; md; md = md->next) {
- if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
- MirrorModifierData *mmd = (MirrorModifierData *)md;
-
- if (mmd->flag & MOD_MIR_CLIPPING) {
- axis = 0;
- if (mmd->flag & MOD_MIR_AXIS_X) {
- axis |= 1;
- tolerance[0] = mmd->tolerance;
- }
- if (mmd->flag & MOD_MIR_AXIS_Y) {
- axis |= 2;
- tolerance[1] = mmd->tolerance;
- }
- if (mmd->flag & MOD_MIR_AXIS_Z) {
- axis |= 4;
- tolerance[2] = mmd->tolerance;
- }
- if (axis) {
- float mtx[4][4], imtx[4][4];
- int i;
+ Object *ob = tc->obedit;
+ ModifierData *md = ob->modifiers.first;
+ float tolerance[3] = {0.0f, 0.0f, 0.0f};
+ int axis = 0;
+
+ for (; md; md = md->next) {
+ if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
+ MirrorModifierData *mmd = (MirrorModifierData *)md;
+
+ if (mmd->flag & MOD_MIR_CLIPPING) {
+ axis = 0;
+ if (mmd->flag & MOD_MIR_AXIS_X) {
+ axis |= 1;
+ tolerance[0] = mmd->tolerance;
+ }
+ if (mmd->flag & MOD_MIR_AXIS_Y) {
+ axis |= 2;
+ tolerance[1] = mmd->tolerance;
+ }
+ if (mmd->flag & MOD_MIR_AXIS_Z) {
+ axis |= 4;
+ tolerance[2] = mmd->tolerance;
+ }
+ if (axis) {
+ float mtx[4][4], imtx[4][4];
+ int i;
- if (mmd->mirror_ob) {
- float obinv[4][4];
+ if (mmd->mirror_ob) {
+ float obinv[4][4];
- invert_m4_m4(obinv, mmd->mirror_ob->obmat);
- mul_m4_m4m4(mtx, obinv, ob->obmat);
- invert_m4_m4(imtx, mtx);
- }
+ invert_m4_m4(obinv, mmd->mirror_ob->obmat);
+ mul_m4_m4m4(mtx, obinv, ob->obmat);
+ invert_m4_m4(imtx, mtx);
+ }
- TransData *td = tc->data;
- for (i = 0; i < tc->data_len; i++, td++) {
- int clip;
- float loc[3], iloc[3];
+ TransData *td = tc->data;
+ for (i = 0; i < tc->data_len; i++, td++) {
+ int clip;
+ float loc[3], iloc[3];
- if (td->loc == NULL) {
- break;
- }
+ if (td->loc == NULL) {
+ break;
+ }
- if (td->flag & TD_SKIP) {
- continue;
- }
+ if (td->flag & TD_SKIP) {
+ continue;
+ }
- copy_v3_v3(loc, td->loc);
- copy_v3_v3(iloc, td->iloc);
+ copy_v3_v3(loc, td->loc);
+ copy_v3_v3(iloc, td->iloc);
- if (mmd->mirror_ob) {
- mul_m4_v3(mtx, loc);
- mul_m4_v3(mtx, iloc);
- }
+ if (mmd->mirror_ob) {
+ mul_m4_v3(mtx, loc);
+ mul_m4_v3(mtx, iloc);
+ }
- clip = 0;
- if (axis & 1) {
- if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) {
- loc[0] = 0.0f;
- clip = 1;
- }
+ clip = 0;
+ if (axis & 1) {
+ if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) {
+ loc[0] = 0.0f;
+ clip = 1;
}
+ }
- if (axis & 2) {
- if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) {
- loc[1] = 0.0f;
- clip = 1;
- }
+ if (axis & 2) {
+ if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) {
+ loc[1] = 0.0f;
+ clip = 1;
}
- if (axis & 4) {
- if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) {
- loc[2] = 0.0f;
- clip = 1;
- }
+ }
+ if (axis & 4) {
+ if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) {
+ loc[2] = 0.0f;
+ clip = 1;
}
- if (clip) {
- if (mmd->mirror_ob) {
- mul_m4_v3(imtx, loc);
- }
- copy_v3_v3(td->loc, loc);
+ }
+ if (clip) {
+ if (mmd->mirror_ob) {
+ mul_m4_v3(imtx, loc);
}
+ copy_v3_v3(td->loc, loc);
}
}
}
@@ -1644,38 +1666,6 @@ void animrecord_check_state(TransInfo *t, struct Object *ob)
}
}
-static void recalcData_cursor_image(TransInfo *t)
-{
- TransDataContainer *tc = t->data_container;
- TransData *td = tc->data;
- float aspect_inv[2];
-
- aspect_inv[0] = 1.0f / t->aspect[0];
- aspect_inv[1] = 1.0f / t->aspect[1];
-
- td->loc[0] = td->loc[0] * aspect_inv[0];
- td->loc[1] = td->loc[1] * aspect_inv[1];
-
- DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
-}
-
-static void recalcData_cursor(TransInfo *t)
-{
- DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
-}
-
-static void recalcData_obedit(TransInfo *t)
-{
- if (t->state != TRANS_CANCEL) {
- applyProject(t);
- }
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (tc->data_len) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
- }
- }
-}
-
/* called for updating while transform acts, once per redraw */
void recalcData(TransInfo *t)
{
@@ -1708,9 +1698,11 @@ void recalcData(TransInfo *t)
recalcData_mask_common(t);
break;
case TC_MESH_VERTS:
- case TC_MESH_EDGES:
recalcData_mesh(t);
break;
+ case TC_MESH_EDGES:
+ recalcData_mesh_edge(t);
+ break;
case TC_MESH_SKIN:
recalcData_mesh_skin(t);
break;
@@ -1742,7 +1734,7 @@ void recalcData(TransInfo *t)
recalcData_tracking(t);
break;
case TC_MBALL_VERTS:
- recalcData_obedit(t);
+ recalcData_mball(t);
break;
case TC_LATTICE_VERTS:
recalcData_lattice(t);
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 36a51d57f64..918ce0739ed 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -45,7 +45,7 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
void clipUVData(TransInfo *t);
/* transform_convert_mesh.c */
-void mesh_customdatacorrect_init(TransInfo *t);
+void transform_convert_mesh_customdatacorrect_init(TransInfo *t);
/* transform_convert_sequencer.c */
int transform_convert_sequencer_get_snap_bound(TransInfo *t);
@@ -62,7 +62,7 @@ void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic);
struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt);
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe);
bool FrameOnMouseSide(char side, float frame, float cframe);
-void clipMirrorModifier(TransInfo *t);
+void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc);
void animrecord_check_state(TransInfo *t, struct Object *ob);
/* transform_convert_action.c */
@@ -84,6 +84,8 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t);
/* transform_convert_cursor.c */
void createTransCursor_image(TransInfo *t);
void createTransCursor_view3d(TransInfo *t);
+void recalcData_cursor_image(TransInfo *t);
+void recalcData_cursor(TransInfo *t);
/* transform_convert_curve.c */
void createTransCurveVerts(TransInfo *t);
@@ -109,6 +111,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t);
/* transform_convert_mball.c */
void createTransMBallVerts(TransInfo *t);
+void recalcData_mball(TransInfo *t);
/* transform_convert_mesh.c */
struct TransIslandData {
@@ -166,6 +169,7 @@ void special_aftertrans_update__mesh(bContext *C, TransInfo *t);
/* transform_convert_mesh_edge.c */
void createTransEdge(TransInfo *t);
+void recalcData_mesh_edge(TransInfo *t);
/* transform_convert_mesh_skin.c */
void createTransMeshSkin(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index a4edf51ffee..aaea9d05f84 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -99,15 +99,7 @@ static void autokeyframe_pose(
bPoseChannel *pchan;
FCurve *fcu;
- /* TODO: this should probably be done per channel instead. */
if (!autokeyframe_cfra_can_key(scene, id)) {
- /* tag channels that should have unkeyed data */
- for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->bone->flag & BONE_TRANSFORM) {
- /* tag this channel */
- pchan->bone->flag |= BONE_UNKEYED;
- }
- }
return;
}
@@ -139,9 +131,6 @@ static void autokeyframe_pose(
ListBase dsources = {NULL, NULL};
- /* clear any 'unkeyed' flag it may have */
- pchan->bone->flag &= ~BONE_UNKEYED;
-
/* add datasource override for the camera object */
ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan);
@@ -1279,9 +1268,6 @@ void recalcData_edit_armature(TransInfo *t)
restoreBones(tc);
}
}
-
- /* Tag for redraw/invalidate overlay cache. */
- DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
}
}
diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c
index 67d85f9610b..1f3eff31205 100644
--- a/source/blender/editors/transform/transform_convert_cursor.c
+++ b/source/blender/editors/transform/transform_convert_cursor.c
@@ -134,3 +134,29 @@ void createTransCursor_view3d(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Recalc Cursor
+ * \{ */
+
+void recalcData_cursor_image(TransInfo *t)
+{
+ TransDataContainer *tc = t->data_container;
+ TransData *td = tc->data;
+ float aspect_inv[2];
+
+ aspect_inv[0] = 1.0f / t->aspect[0];
+ aspect_inv[1] = 1.0f / t->aspect[1];
+
+ td->loc[0] = td->loc[0] * aspect_inv[0];
+ td->loc[1] = td->loc[1] * aspect_inv[1];
+
+ DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+void recalcData_cursor(TransInfo *t)
+{
+ DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c
index bef3eca0d9d..e57fd85470f 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -441,7 +441,6 @@ void createTransCurveVerts(TransInfo *t)
void recalcData_curve(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- clipMirrorModifier(t);
applyProject(t);
}
@@ -450,7 +449,7 @@ void recalcData_curve(TransInfo *t)
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
Nurb *nu = nurbs->first;
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
if (t->state == TRANS_CANCEL) {
while (nu) {
@@ -460,12 +459,11 @@ void recalcData_curve(TransInfo *t)
}
}
else {
- /* Normal updating */
- while (nu) {
- BKE_nurb_test_2d(nu);
- BKE_nurb_handles_calc(nu);
- nu = nu->next;
- }
+ /* Apply clipping after so we never project past the clip plane T25423. */
+ transform_convert_clip_mirror_modifier_apply(tc);
+
+ /* Normal updating. */
+ BKE_curve_dimension_update(cu);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index 45df0e66691..4932a5f8d23 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -78,14 +78,12 @@ static short get_bezt_sel_triple_flag(BezTriple *bezt, const bool handles_visibl
flag = ((bezt->f1 & SELECT) ? SEL_F1 : 0) | ((bezt->f2 & SELECT) ? SEL_F2 : 0) |
((bezt->f3 & SELECT) ? SEL_F3 : 0);
}
- else {
- if (bezt->f2 & SELECT) {
- flag = SEL_ALL;
- }
+ else if (bezt->f2 & SELECT) {
+ flag = SEL_ALL;
}
/* Special case for auto & aligned handles */
- if (flag != SEL_ALL && flag & SEL_F2) {
+ if ((flag != SEL_ALL) && (flag & SEL_F2)) {
if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
flag = SEL_ALL;
}
@@ -316,7 +314,7 @@ static void createTransGPencil_curves(bContext *C,
}
}
else if (handles_visible) {
- if (BEZT_ISSEL_IDX(bezt, j)) {
+ if (sel) {
td->flag = TD_SELECTED;
}
else {
diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c
index 20ac7dcb998..fbfce41d555 100644
--- a/source/blender/editors/transform/transform_convert_lattice.c
+++ b/source/blender/editors/transform/transform_convert_lattice.c
@@ -122,7 +122,7 @@ void recalcData_lattice(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
Lattice *la = tc->obedit->data;
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
if (la->editlatt->latt->flag & LT_OUTSIDE) {
outside_lattice(la->editlatt->latt);
}
diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c
index 6f5c0318054..f38f3ccf421 100644
--- a/source/blender/editors/transform/transform_convert_mball.c
+++ b/source/blender/editors/transform/transform_convert_mball.c
@@ -30,6 +30,8 @@
#include "BKE_context.h"
#include "transform.h"
+#include "transform_snap.h"
+
#include "transform_convert.h"
/* -------------------------------------------------------------------- */
@@ -128,3 +130,21 @@ void createTransMBallVerts(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Recalc Meta Ball
+ * \{ */
+
+void recalcData_mball(TransInfo *t)
+{
+ if (t->state != TRANS_CANCEL) {
+ applyProject(t);
+ }
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->data_len) {
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 93c36645873..422370cb13b 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -48,7 +48,637 @@
#include "transform_convert.h"
+/* -------------------------------------------------------------------- */
+/** \name Container TransCustomData Creation
+ * \{ */
+
+static void tc_mesh_customdata_free_fn(struct TransInfo *t,
+ struct TransDataContainer *tc,
+ struct TransCustomData *custom_data);
+
+struct TransCustomDataLayer;
+static void tc_mesh_customdatacorrect_free(struct TransCustomDataLayer *tcld);
+
+struct TransCustomDataMesh {
+ struct TransCustomDataLayer *cd_layer_correct;
+ struct {
+ struct BMPartialUpdate *cache;
+
+ /** The size of proportional editing used for `partial_update_cache`. */
+ float prop_size;
+ /** The size of proportional editing for the last update. */
+ float prop_size_prev;
+ } partial_update;
+};
+
+static struct TransCustomDataMesh *tc_mesh_customdata_ensure(TransDataContainer *tc)
+{
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ BLI_assert(tc->custom.type.data == NULL ||
+ tc->custom.type.free_cb == tc_mesh_customdata_free_fn);
+ if (tc->custom.type.data == NULL) {
+ tc->custom.type.data = MEM_callocN(sizeof(struct TransCustomDataMesh), __func__);
+ tc->custom.type.free_cb = tc_mesh_customdata_free_fn;
+ tcmd = tc->custom.type.data;
+ }
+ return tcmd;
+}
+
+static void tc_mesh_customdata_free(struct TransCustomDataMesh *tcmd)
+{
+ if (tcmd->cd_layer_correct != NULL) {
+ tc_mesh_customdatacorrect_free(tcmd->cd_layer_correct);
+ }
+
+ if (tcmd->partial_update.cache != NULL) {
+ BM_mesh_partial_destroy(tcmd->partial_update.cache);
+ }
+
+ MEM_freeN(tcmd);
+}
+
+static void tc_mesh_customdata_free_fn(struct TransInfo *UNUSED(t),
+ struct TransDataContainer *UNUSED(tc),
+ struct TransCustomData *custom_data)
+{
+ struct TransCustomDataMesh *tcmd = custom_data->data;
+ tc_mesh_customdata_free(tcmd);
+ custom_data->data = NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData TransCustomDataLayer Creation
+ * \{ */
+
+struct TransCustomDataMergeGroup {
+ /** map {BMVert: TransCustomDataLayerVert} */
+ struct LinkNode **cd_loop_groups;
+};
+
+struct TransCustomDataLayer {
+ BMesh *bm;
+ struct MemArena *arena;
+
+ struct GHash *origfaces;
+ struct BMesh *bm_origfaces;
+
+ /* Special handle for multi-resolution. */
+ int cd_loop_mdisp_offset;
+
+ /* Optionally merge custom-data groups (this keeps UVs connected for example). */
+ struct {
+ /** map {BMVert: TransDataBasic} */
+ struct GHash *origverts;
+ struct TransCustomDataMergeGroup *data;
+ int data_len;
+ /** Array size of 'layer_math_map_len'
+ * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */
+ int *customdatalayer_map;
+ /** Number of math BMLoop layers. */
+ int customdatalayer_map_len;
+ } merge_group;
+
+ bool use_merge_group;
+};
+
#define USE_FACE_SUBSTITUTE
+#ifdef USE_FACE_SUBSTITUTE
+# define FACE_SUBSTITUTE_INDEX INT_MIN
+
+/**
+ * Search for a neighboring face with area and preferably without selected vertex.
+ * Used to replace area-less faces in custom-data correction.
+ */
+static BMFace *tc_mesh_customdatacorrect_find_best_face_substitute(BMFace *f)
+{
+ BMFace *best_face = NULL;
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ BMLoop *l_radial_next = l->radial_next;
+ BMFace *f_test = l_radial_next->f;
+ if (f_test == f) {
+ continue;
+ }
+ if (is_zero_v3(f_test->no)) {
+ continue;
+ }
+
+ /* Check the loops edge isn't selected. */
+ if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) &&
+ !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) {
+ /* Prefer edges with unselected vertices.
+ * Useful for extrude. */
+ best_face = f_test;
+ break;
+ }
+ if (best_face == NULL) {
+ best_face = f_test;
+ }
+ }
+ return best_face;
+}
+
+static void tc_mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld,
+ BMFace *f,
+ BMFace *f_copy)
+{
+ BLI_assert(is_zero_v3(f->no));
+ BMesh *bm = tcld->bm;
+ /* It is impossible to calculate the loops weights of a face without area.
+ * Find a substitute. */
+ BMFace *f_substitute = tc_mesh_customdatacorrect_find_best_face_substitute(f);
+ if (f_substitute) {
+ /* Copy the custom-data from the substitute face. */
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false);
+ } while ((l_iter = l_iter->next) != l_first);
+
+ /* Use the substitute face as the reference during the transformation. */
+ BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true);
+
+ /* Hack: reference substitute face in `f_copy->no`.
+ * `tcld->origfaces` is already used to restore the initial value. */
+ BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX);
+ *((BMFace **)&f_copy->no[0]) = f_substitute_copy;
+ }
+}
+
+static BMFace *tc_mesh_customdatacorrect_face_substitute_get(BMFace *f_copy)
+{
+ BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX);
+ return *((BMFace **)&f_copy->no[0]);
+}
+
+#endif /* USE_FACE_SUBSTITUTE */
+
+static void tc_mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld,
+ struct TransDataBasic *td,
+ const int index)
+{
+ BMesh *bm = tcld->bm;
+ BMVert *v = td->extra;
+ BMIter liter;
+ int j, l_num;
+ float *loop_weights;
+
+ // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
+ BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
+ l_num = liter.count;
+ loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL;
+ for (j = 0; j < l_num; j++) {
+ BMLoop *l = BM_iter_step(&liter);
+ BMLoop *l_prev, *l_next;
+
+ /* Generic custom-data correction. Copy face data. */
+ void **val_p;
+ if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) {
+ BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true);
+ *val_p = f_copy;
+#ifdef USE_FACE_SUBSTITUTE
+ if (is_zero_v3(l->f->no)) {
+ tc_mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy);
+ }
+#endif
+ }
+
+ if (tcld->use_merge_group) {
+ if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
+ (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
+ loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
+ }
+ else {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ }
+
+ if (tcld->use_merge_group) {
+ /* Store cd_loop_groups. */
+ struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index];
+ if (l_num != 0) {
+ merge_data->cd_loop_groups = BLI_memarena_alloc(
+ tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *));
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ const int layer_nr = tcld->merge_group.customdatalayer_map[j];
+ merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
+ bm, v, layer_nr, loop_weights, tcld->arena);
+ }
+ }
+ else {
+ merge_data->cd_loop_groups = NULL;
+ }
+
+ BLI_ghash_insert(tcld->merge_group.origverts, v, td);
+ }
+}
+
+static void tc_mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc),
+ struct TransCustomDataLayer *tcld)
+{
+ BMesh *bm = tcld->bm;
+
+ struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
+ struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
+ &((struct BMeshCreateParams){
+ .use_toolflags = false,
+ }));
+
+ /* We need to have matching loop custom-data. */
+ BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL);
+
+ tcld->origfaces = origfaces;
+ tcld->bm_origfaces = bm_origfaces;
+
+ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+ tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
+}
+
+static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc,
+ struct TransCustomDataLayer *tcld)
+{
+ BMesh *bm = tcld->bm;
+ BLI_assert(CustomData_has_math(&bm->ldata));
+
+ /* TODO: We don't need `layer_math_map` when there are no loops linked
+ * to one of the sliding vertices. */
+
+ /* Over allocate, only 'math' layers are indexed. */
+ int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__);
+ int layer_math_map_len = 0;
+ for (int i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i)) {
+ customdatalayer_map[layer_math_map_len++] = i;
+ }
+ }
+ BLI_assert(layer_math_map_len != 0);
+
+ tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len;
+ tcld->merge_group.customdatalayer_map = customdatalayer_map;
+ tcld->merge_group.customdatalayer_map_len = layer_math_map_len;
+ tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len);
+ tcld->merge_group.data = BLI_memarena_alloc(
+ tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data));
+}
+
+static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create_impl(
+ TransDataContainer *tc, const bool use_merge_group)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ BMesh *bm = em->bm;
+
+ if (bm->shapenr > 1) {
+ /* Don't do this at all for non-basis shape keys, too easy to
+ * accidentally break uv maps or vertex colors then */
+ /* create copies of faces for custom-data projection. */
+ return NULL;
+ }
+ if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
+ /* There is no custom-data to correct. */
+ return NULL;
+ }
+
+ struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__);
+ tcld->bm = bm;
+ tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+
+ /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */
+ tcld->cd_loop_mdisp_offset = -1;
+ tcld->use_merge_group = use_merge_group;
+
+ tc_mesh_customdatacorrect_init_container_generic(tc, tcld);
+
+ if (tcld->use_merge_group) {
+ tc_mesh_customdatacorrect_init_container_merge_group(tc, tcld);
+ }
+
+ {
+ /* Setup Verts. */
+ int i = 0;
+
+ TransData *tob = tc->data;
+ for (int j = tc->data_len; j--; tob++, i++) {
+ tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i);
+ }
+
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int j = tc->data_mirror_len; j--; td_mirror++, i++) {
+ tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i);
+ }
+ }
+
+ return tcld;
+}
+
+static void tc_mesh_customdatacorrect_create(TransDataContainer *tc, const bool use_merge_group)
+{
+ struct TransCustomDataLayer *customdatacorrect;
+ customdatacorrect = tc_mesh_customdatacorrect_create_impl(tc, use_merge_group);
+
+ if (!customdatacorrect) {
+ return;
+ }
+
+ struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc);
+ BLI_assert(tcmd->cd_layer_correct == NULL);
+ tcmd->cd_layer_correct = customdatacorrect;
+}
+
+static void tc_mesh_customdatacorrect_free(struct TransCustomDataLayer *tcld)
+{
+ bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+
+ if (tcld->bm_origfaces) {
+ BM_mesh_free(tcld->bm_origfaces);
+ }
+ if (tcld->origfaces) {
+ BLI_ghash_free(tcld->origfaces, NULL, NULL);
+ }
+ if (tcld->merge_group.origverts) {
+ BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL);
+ }
+ if (tcld->arena) {
+ BLI_memarena_free(tcld->arena);
+ }
+ if (tcld->merge_group.customdatalayer_map) {
+ MEM_freeN(tcld->merge_group.customdatalayer_map);
+ }
+
+ MEM_freeN(tcld);
+}
+
+void transform_convert_mesh_customdatacorrect_init(TransInfo *t)
+{
+ bool use_merge_group = false;
+ if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
+ if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) {
+ /* No custom-data correction. */
+ return;
+ }
+ use_merge_group = true;
+ }
+ else if (ELEM(t->mode,
+ TFM_TRANSLATION,
+ TFM_ROTATION,
+ TFM_RESIZE,
+ TFM_TOSPHERE,
+ TFM_SHEAR,
+ TFM_BEND,
+ TFM_SHRINKFATTEN,
+ TFM_TRACKBALL,
+ TFM_PUSHPULL,
+ TFM_ALIGN)) {
+ {
+ if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
+ /* No custom-data correction. */
+ return;
+ }
+ use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0;
+ }
+ }
+ else {
+ return;
+ }
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->custom.type.data != NULL) {
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ if (tcmd && tcmd->cd_layer_correct) {
+ tc_mesh_customdatacorrect_free(tcmd->cd_layer_correct);
+ tcmd->cd_layer_correct = NULL;
+ }
+ }
+
+ tc_mesh_customdatacorrect_create(tc, use_merge_group);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData Layer Correction Apply
+ * \{ */
+
+/**
+ * If we're sliding the vert, return its original location, if not, the current location is good.
+ */
+static const float *tc_mesh_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v)
+{
+ TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v);
+ return td ? td->iloc : v->co;
+}
+
+static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld,
+ struct TransDataBasic *td,
+ struct TransCustomDataMergeGroup *merge_data,
+ bool do_loop_mdisps)
+{
+ BMesh *bm = tcld->bm;
+ BMVert *v = td->extra;
+ const float *co_orig_3d = td->iloc;
+
+ BMIter liter;
+ int j, l_num;
+ float *loop_weights;
+ const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON);
+ const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len;
+ const float *v_proj_axis = v->no;
+ /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */
+ float v_proj[3][3];
+
+ if (do_loop_weight) {
+ project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis);
+ }
+
+ // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT)
+ BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
+ l_num = liter.count;
+ loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL;
+ for (j = 0; j < l_num; j++) {
+ BMFace *f_copy; /* the copy of 'f' */
+ BMLoop *l = BM_iter_step(&liter);
+
+ f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
+
+#ifdef USE_FACE_SUBSTITUTE
+ /* In some faces it is not possible to calculate interpolation,
+ * so we use a substitute. */
+ if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) {
+ f_copy = tc_mesh_customdatacorrect_face_substitute_get(f_copy);
+ }
+#endif
+
+ /* only loop data, no vertex data since that contains shape keys,
+ * and we do not want to mess up other shape keys */
+ BM_loop_interp_from_face(bm, l, f_copy, false, false);
+
+ /* weight the loop */
+ if (do_loop_weight) {
+ const float eps = 1.0e-8f;
+ const BMLoop *l_prev = l->prev;
+ const BMLoop *l_next = l->next;
+ const float *co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v);
+ const float *co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v);
+ bool co_prev_ok;
+ bool co_next_ok;
+
+ /* In the unlikely case that we're next to a zero length edge -
+ * walk around the to the next.
+ *
+ * Since we only need to check if the vertex is in this corner,
+ * its not important _which_ loop - as long as its not overlapping
+ * 'sv->co_orig_3d', see: T45096. */
+ project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
+ while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
+ ((l_prev = l_prev->prev) != l->next))) {
+ co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v);
+ project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
+ }
+ project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
+ while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
+ ((l_next = l_next->next) != l->prev))) {
+ co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v);
+ project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
+ }
+
+ if (co_prev_ok && co_next_ok) {
+ const float dist = dist_signed_squared_to_corner_v3v3v3(
+ v->co, UNPACK3(v_proj), v_proj_axis);
+
+ loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
+ if (UNLIKELY(!isfinite(loop_weights[j]))) {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ else {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ }
+
+ if (tcld->use_merge_group) {
+ struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
+ if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
+ if (do_loop_weight) {
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ BM_vert_loop_groups_data_layer_merge_weights(
+ bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
+ }
+ }
+ else {
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ BM_vert_loop_groups_data_layer_merge(
+ bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
+ }
+ }
+ }
+ }
+
+ /* Special handling for multires
+ *
+ * Interpolate from every other loop (not ideal)
+ * However values will only be taken from loops which overlap other mdisps.
+ */
+ const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
+ if (update_loop_mdisps) {
+ float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
+ BMLoop *l;
+
+ BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
+ BM_face_calc_center_median(l->f, faces_center[j]);
+ }
+
+ BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
+ BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
+ float f_copy_center[3];
+ BMIter liter_other;
+ BMLoop *l_other;
+ int j_other;
+
+ BM_face_calc_center_median(f_copy, f_copy_center);
+
+ BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) {
+ BM_face_interp_multires_ex(bm,
+ l_other->f,
+ f_copy,
+ faces_center[j_other],
+ f_copy_center,
+ tcld->cd_loop_mdisp_offset);
+ }
+ }
+ }
+}
+
+static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final)
+{
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL;
+ if (tcld == NULL) {
+ return;
+ }
+ const bool use_merge_group = tcld->use_merge_group;
+
+ struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
+ TransData *tob = tc->data;
+ for (int i = tc->data_len; i--; tob++) {
+ tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
+
+ if (use_merge_group) {
+ merge_data++;
+ }
+ }
+
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int i = tc->data_mirror_len; i--; td_mirror++) {
+ tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
+
+ if (use_merge_group) {
+ merge_data++;
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData Layer Correction Restore
+ * \{ */
+
+static void tc_mesh_customdatacorrect_restore(struct TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL;
+ if (!tcld) {
+ continue;
+ }
+
+ BMesh *bm = tcld->bm;
+ BMesh *bm_copy = tcld->bm_origfaces;
+
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tcld->origfaces) {
+ BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
+ BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter);
+ BLI_assert(f->len == f_copy->len);
+
+ BMLoop *l_iter, *l_first, *l_copy;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ l_copy = BM_FACE_FIRST_LOOP(f_copy);
+ do {
+ /* TODO: Restore only the elements that transform. */
+ BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter);
+ l_copy = l_copy->next;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+}
+
+/** \} */
/* -------------------------------------------------------------------- */
/** \name Island Creation
@@ -384,6 +1014,14 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
BMEdge *e;
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
+
+ /* Always clear to satisfy the assert, also predictable to leave in cleared state. */
+ BM_elem_flag_disable(e, tag_queued);
+
+ if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+
BMVert *v1 = e->v1;
BMVert *v2 = e->v2;
int i1 = BM_elem_index_get(v1);
@@ -392,7 +1030,6 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
if (dists[i1] != FLT_MAX || dists[i2] != FLT_MAX) {
BLI_LINKSTACK_PUSH(queue, e);
}
- BM_elem_flag_disable(e, tag_queued);
BM_elem_flag_set(e, tag_loose, bmesh_test_loose_edge(e));
}
}
@@ -420,6 +1057,7 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
BMIter eiter;
BM_ITER_ELEM (e_other, &eiter, v2, BM_EDGES_OF_VERT) {
if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 &&
+ !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) &&
(BM_elem_flag_test(e, tag_loose) || BM_elem_flag_test(e_other, tag_loose))) {
BM_elem_flag_enable(e_other, tag_queued);
BLI_LINKSTACK_PUSH(queue_next, e_other);
@@ -433,6 +1071,11 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
BMLoop *l;
BMIter liter;
BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
+ if (BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+ /* Don't check hidden edges or vertices in this loop
+ * since any hidden edge causes the face to be hidden too. */
for (BMLoop *l_other = l->next->next; l_other != l; l_other = l_other->next) {
BMVert *v_other = l_other->v;
BLI_assert(!ELEM(v_other, v1, v2));
@@ -445,6 +1088,7 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
BMIter eiter;
BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) {
if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 &&
+ !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) &&
(BM_elem_flag_test(e_other, tag_loose) ||
dists[BM_elem_index_get(BM_edge_other_vert(e_other, v_other))] != FLT_MAX)) {
BM_elem_flag_enable(e_other, tag_queued);
@@ -727,10 +1371,10 @@ void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_cr
/** \name Edit Mesh Verts Transform Creation
* \{ */
-static void transdata_center_get(const struct TransIslandData *island_data,
- const int island_index,
- const float iloc[3],
- float r_center[3])
+static void tc_mesh_transdata_center_copy(const struct TransIslandData *island_data,
+ const int island_index,
+ const float iloc[3],
+ float r_center[3])
{
if (island_data->center && island_index != -1) {
copy_v3_v3(r_center, island_data->center[island_index]);
@@ -769,7 +1413,7 @@ static void VertsToTransData(TransInfo *t,
no = eve->no;
}
- transdata_center_get(island_data, island_index, td->iloc, td->center);
+ tc_mesh_transdata_center_copy(island_data, island_index, td->iloc, td->center);
if ((island_index != -1) && island_data->axismtx) {
copy_m3_m3(td->axismtx, island_data->axismtx[island_index]);
@@ -959,7 +1603,8 @@ void createTransEditVerts(TransInfo *t)
copy_v3_v3(td_mirror->iloc, eve->co);
td_mirror->flag = mirror_data.vert_map[a].flag;
td_mirror->loc_src = v_src->co;
- transdata_center_get(&island_data, island_index, td_mirror->iloc, td_mirror->center);
+ tc_mesh_transdata_center_copy(
+ &island_data, island_index, td_mirror->iloc, td_mirror->center);
td_mirror++;
}
@@ -1031,590 +1676,133 @@ void createTransEditVerts(TransInfo *t)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name CustomData Layer Correction
+/** \name Recalc Mesh Data
* \{ */
-struct TransCustomDataMergeGroup {
- /** map {BMVert: TransCustomDataLayerVert} */
- struct LinkNode **cd_loop_groups;
-};
-
-struct TransCustomDataLayer {
- BMesh *bm;
- struct MemArena *arena;
-
- struct GHash *origfaces;
- struct BMesh *bm_origfaces;
-
- /* Special handle for multi-resolution. */
- int cd_loop_mdisp_offset;
-
- /* Optionally merge custom-data groups (this keeps UVs connected for example). */
- struct {
- /** map {BMVert: TransDataBasic} */
- struct GHash *origverts;
- struct TransCustomDataMergeGroup *data;
- int data_len;
- /** Array size of 'layer_math_map_len'
- * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */
- int *customdatalayer_map;
- /** Number of math BMLoop layers. */
- int customdatalayer_map_len;
- } merge_group;
-
- bool use_merge_group;
-};
-
-static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t),
- struct TransDataContainer *UNUSED(tc),
- struct TransCustomData *custom_data)
+static bool bm_vert_tag_filter_fn(BMVert *v, void *UNUSED(user_data))
{
- struct TransCustomDataLayer *tcld = custom_data->data;
- bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
-
- if (tcld->bm_origfaces) {
- BM_mesh_free(tcld->bm_origfaces);
- }
- if (tcld->origfaces) {
- BLI_ghash_free(tcld->origfaces, NULL, NULL);
- }
- if (tcld->merge_group.origverts) {
- BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL);
- }
- if (tcld->arena) {
- BLI_memarena_free(tcld->arena);
- }
- if (tcld->merge_group.customdatalayer_map) {
- MEM_freeN(tcld->merge_group.customdatalayer_map);
- }
-
- MEM_freeN(tcld);
- custom_data->data = NULL;
-}
-
-#ifdef USE_FACE_SUBSTITUTE
-
-# define FACE_SUBSTITUTE_INDEX INT_MIN
-
-/**
- * Search for a neighboring face with area and preferably without selected vertex.
- * Used to replace area-less faces in custom-data correction.
- */
-static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f)
-{
- BMFace *best_face = NULL;
- BMLoop *l;
- BMIter liter;
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- BMLoop *l_radial_next = l->radial_next;
- BMFace *f_test = l_radial_next->f;
- if (f_test == f) {
- continue;
- }
- if (is_zero_v3(f_test->no)) {
- continue;
- }
-
- /* Check the loops edge isn't selected. */
- if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) &&
- !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) {
- /* Prefer edges with unselected vertices.
- * Useful for extrude. */
- best_face = f_test;
- break;
- }
- if (best_face == NULL) {
- best_face = f_test;
- }
- }
- return best_face;
-}
-
-static void mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld,
- BMFace *f,
- BMFace *f_copy)
-{
- BLI_assert(is_zero_v3(f->no));
- BMesh *bm = tcld->bm;
- /* It is impossible to calculate the loops weights of a face without area.
- * Find a substitute. */
- BMFace *f_substitute = mesh_customdatacorrect_find_best_face_substitute(f);
- if (f_substitute) {
- /* Copy the custom-data from the substitute face. */
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false);
- } while ((l_iter = l_iter->next) != l_first);
-
- /* Use the substitute face as the reference during the transformation. */
- BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true);
-
- /* Hack: reference substitute face in `f_copy->no`.
- * `tcld->origfaces` is already used to restore the initial value. */
- BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX);
- *((BMFace **)&f_copy->no[0]) = f_substitute_copy;
- }
+ return BM_elem_flag_test(v, BM_ELEM_TAG);
}
-static BMFace *mesh_customdatacorrect_face_substitute_get(BMFace *f_copy)
+static BMPartialUpdate *tc_mesh_ensure_partial_update(TransInfo *t, TransDataContainer *tc)
{
- BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX);
- return *((BMFace **)&f_copy->no[0]);
-}
-
-#endif /* USE_FACE_SUBSTITUTE */
+ struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc);
-static void mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld,
- struct TransDataBasic *td,
- const int index)
-{
- BMesh *bm = tcld->bm;
- BMVert *v = td->extra;
- BMIter liter;
- int j, l_num;
- float *loop_weights;
+ if (tcmd->partial_update.cache) {
- // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
- BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
- l_num = liter.count;
- loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL;
- for (j = 0; j < l_num; j++) {
- BMLoop *l = BM_iter_step(&liter);
- BMLoop *l_prev, *l_next;
+ /* Recalculate partial update data when the proportional editing size changes.
+ *
+ * Note that decreasing the proportional editing size requires the existing
+ * partial data is used before recreating this partial data at the smaller size.
+ * Since excluding geometry from being transformed requires an update.
+ *
+ * Extra logic is needed to account for this situation. */
- /* Generic custom-data correction. Copy face data. */
- void **val_p;
- if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) {
- BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true);
- *val_p = f_copy;
-#ifdef USE_FACE_SUBSTITUTE
- if (is_zero_v3(l->f->no)) {
- mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy);
- }
-#endif
+ bool recalc;
+ if (tcmd->partial_update.prop_size_prev < t->prop_size) {
+ /* Size increase, simply recalculate. */
+ recalc = true;
}
-
- if (tcld->use_merge_group) {
- if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
- (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
- loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
- }
- else {
- loop_weights[j] = 0.0f;
- }
+ else if (tcmd->partial_update.prop_size_prev > t->prop_size) {
+ /* Size decreased, first use this partial data since reducing the size will transform
+ * geometry which needs recalculating. */
+ tcmd->partial_update.prop_size_prev = t->prop_size;
+ recalc = false;
}
- }
-
- if (tcld->use_merge_group) {
- /* Store cd_loop_groups. */
- struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index];
- if (l_num != 0) {
- merge_data->cd_loop_groups = BLI_memarena_alloc(
- tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *));
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- const int layer_nr = tcld->merge_group.customdatalayer_map[j];
- merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
- bm, v, layer_nr, loop_weights, tcld->arena);
- }
+ else if (tcmd->partial_update.prop_size != t->prop_size) {
+ BLI_assert(tcmd->partial_update.prop_size > tcmd->partial_update.prop_size_prev);
+ recalc = true;
}
else {
- merge_data->cd_loop_groups = NULL;
+ BLI_assert(t->prop_size == tcmd->partial_update.prop_size_prev);
+ recalc = false;
}
- BLI_ghash_insert(tcld->merge_group.origverts, v, td);
- }
-}
-
-static void mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc),
- struct TransCustomDataLayer *tcld)
-{
- BMesh *bm = tcld->bm;
-
- struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
- struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
-
- /* We need to have matching loop custom-data. */
- BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL);
-
- tcld->origfaces = origfaces;
- tcld->bm_origfaces = bm_origfaces;
-
- bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
- tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
-}
-
-static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc,
- struct TransCustomDataLayer *tcld)
-{
- BMesh *bm = tcld->bm;
- BLI_assert(CustomData_has_math(&bm->ldata));
-
- /* TODO: We don't need `layer_math_map` when there are no loops linked
- * to one of the sliding vertices. */
-
- /* Over allocate, only 'math' layers are indexed. */
- int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__);
- int layer_math_map_len = 0;
- for (int i = 0; i < bm->ldata.totlayer; i++) {
- if (CustomData_layer_has_math(&bm->ldata, i)) {
- customdatalayer_map[layer_math_map_len++] = i;
+ if (!recalc) {
+ return tcmd->partial_update.cache;
}
- }
- BLI_assert(layer_math_map_len != 0);
-
- tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len;
- tcld->merge_group.customdatalayer_map = customdatalayer_map;
- tcld->merge_group.customdatalayer_map_len = layer_math_map_len;
- tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len);
- tcld->merge_group.data = BLI_memarena_alloc(
- tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data));
-}
-static void mesh_customdatacorrect_init_container(TransDataContainer *tc,
- const bool use_merge_group)
-{
- if (tc->custom.type.data) {
- /* The custom-data correction has been initiated before.
- * Free since some modes have different settings. */
- mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type);
+ BM_mesh_partial_destroy(tcmd->partial_update.cache);
+ tcmd->partial_update.cache = NULL;
}
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
- BMesh *bm = em->bm;
- if (bm->shapenr > 1) {
- /* Don't do this at all for non-basis shape keys, too easy to
- * accidentally break uv maps or vertex colors then */
- /* create copies of faces for custom-data projection. */
- return;
- }
- if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
- /* There is no custom-data to correct. */
- return;
- }
+ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
- struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__);
- tcld->bm = bm;
- tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
-
- /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */
- tcld->cd_loop_mdisp_offset = -1;
- tcld->use_merge_group = use_merge_group;
-
- mesh_customdatacorrect_init_container_generic(tc, tcld);
-
- if (tcld->use_merge_group) {
- mesh_customdatacorrect_init_container_merge_group(tc, tcld);
- }
-
- {
- /* Setup Verts. */
- int i = 0;
-
- TransData *tob = tc->data;
- for (int j = tc->data_len; j--; tob++, i++) {
- mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i);
- }
-
- TransDataMirror *td_mirror = tc->data_mirror;
- for (int j = tc->data_mirror_len; j--; td_mirror++, i++) {
- mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i);
+ int verts_len = 0;
+ int i;
+ TransData *td;
+ for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
+ if (td->factor != 0.0f) {
+ BMVert *v = (BMVert *)td->extra;
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ verts_len += 1;
}
}
- tc->custom.type.data = tcld;
- tc->custom.type.free_cb = mesh_customdatacorrect_free_cb;
-}
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
+ BMVert *v_mirr = (BMVert *)POINTER_OFFSET(td_mirror->loc_src, -offsetof(BMVert, co));
-void mesh_customdatacorrect_init(TransInfo *t)
-{
- bool use_merge_group = false;
- if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
- if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) {
- /* No custom-data correction. */
- return;
- }
- use_merge_group = true;
- }
- else if (ELEM(t->mode,
- TFM_TRANSLATION,
- TFM_ROTATION,
- TFM_RESIZE,
- TFM_TOSPHERE,
- TFM_SHEAR,
- TFM_BEND,
- TFM_SHRINKFATTEN,
- TFM_TRACKBALL,
- TFM_PUSHPULL,
- TFM_ALIGN)) {
- {
- if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
- /* No custom-data correction. */
- return;
- }
- use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0;
+ /* The equality check is to account for the case when topology mirror moves
+ * the vertex from it's original location to match it's symmetrical position,
+ * with proportional editing enabled. */
+ if (BM_elem_flag_test(v_mirr, BM_ELEM_TAG) || !equals_v3v3(td_mirror->loc, td_mirror->iloc)) {
+ BMVert *v_mirr_other = (BMVert *)td_mirror->extra;
+ /* This assert should never fail since there is no overlap
+ * between mirrored vertices and non-mirrored. */
+ BLI_assert(!BM_elem_flag_test(v_mirr_other, BM_ELEM_TAG));
+ BM_elem_flag_enable(v_mirr_other, BM_ELEM_TAG);
+ verts_len += 1;
}
}
- else {
- return;
- }
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- mesh_customdatacorrect_init_container(tc, use_merge_group);
- }
-}
+ tcmd->partial_update.cache = BM_mesh_partial_create_from_verts(em->bm,
+ &(BMPartialUpdate_Params){
+ .do_tessellate = true,
+ .do_normals = true,
+ },
+ verts_len,
+ bm_vert_tag_filter_fn,
+ NULL);
-/**
- * If we're sliding the vert, return its original location, if not, the current location is good.
- */
-static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v)
-{
- TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v);
- return td ? td->iloc : v->co;
+ tcmd->partial_update.prop_size_prev = t->prop_size;
+ tcmd->partial_update.prop_size = t->prop_size;
+
+ return tcmd->partial_update.cache;
}
-static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld,
- struct TransDataBasic *td,
- struct TransCustomDataMergeGroup *merge_data,
- bool do_loop_mdisps)
+static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc)
{
- BMesh *bm = tcld->bm;
- BMVert *v = td->extra;
- const float *co_orig_3d = td->iloc;
-
- BMIter liter;
- int j, l_num;
- float *loop_weights;
- const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON);
- const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len;
- const float *v_proj_axis = v->no;
- /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */
- float v_proj[3][3];
-
- if (do_loop_weight) {
- project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis);
- }
-
- // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT)
- BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
- l_num = liter.count;
- loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL;
- for (j = 0; j < l_num; j++) {
- BMFace *f_copy; /* the copy of 'f' */
- BMLoop *l = BM_iter_step(&liter);
-
- f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
-
-#ifdef USE_FACE_SUBSTITUTE
- /* In some faces it is not possible to calculate interpolation,
- * so we use a substitute. */
- if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) {
- f_copy = mesh_customdatacorrect_face_substitute_get(f_copy);
- }
-#endif
-
- /* only loop data, no vertex data since that contains shape keys,
- * and we do not want to mess up other shape keys */
- BM_loop_interp_from_face(bm, l, f_copy, false, false);
-
- /* weight the loop */
- if (do_loop_weight) {
- const float eps = 1.0e-8f;
- const BMLoop *l_prev = l->prev;
- const BMLoop *l_next = l->next;
- const float *co_prev = trans_vert_orig_co_get(tcld, l_prev->v);
- const float *co_next = trans_vert_orig_co_get(tcld, l_next->v);
- bool co_prev_ok;
- bool co_next_ok;
-
- /* In the unlikely case that we're next to a zero length edge -
- * walk around the to the next.
- *
- * Since we only need to check if the vertex is in this corner,
- * its not important _which_ loop - as long as its not overlapping
- * 'sv->co_orig_3d', see: T45096. */
- project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
- while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
- ((l_prev = l_prev->prev) != l->next))) {
- co_prev = trans_vert_orig_co_get(tcld, l_prev->v);
- project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
- }
- project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
- while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
- ((l_next = l_next->next) != l->prev))) {
- co_next = trans_vert_orig_co_get(tcld, l_next->v);
- project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
- }
-
- if (co_prev_ok && co_next_ok) {
- const float dist = dist_signed_squared_to_corner_v3v3v3(
- v->co, UNPACK3(v_proj), v_proj_axis);
-
- loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
- if (UNLIKELY(!isfinite(loop_weights[j]))) {
- loop_weights[j] = 0.0f;
+ if (tc->use_mirror_axis_any) {
+ int i;
+ TransData *td;
+ for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
+ if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) {
+ if (td->flag & TD_MIRROR_EDGE_X) {
+ td->loc[0] = 0.0f;
}
- }
- else {
- loop_weights[j] = 0.0f;
- }
- }
- }
-
- if (tcld->use_merge_group) {
- struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
- if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
- if (do_loop_weight) {
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- BM_vert_loop_groups_data_layer_merge_weights(
- bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
+ if (td->flag & TD_MIRROR_EDGE_Y) {
+ td->loc[1] = 0.0f;
}
- }
- else {
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- BM_vert_loop_groups_data_layer_merge(
- bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
+ if (td->flag & TD_MIRROR_EDGE_Z) {
+ td->loc[2] = 0.0f;
}
}
}
- }
-
- /* Special handling for multires
- *
- * Interpolate from every other loop (not ideal)
- * However values will only be taken from loops which overlap other mdisps.
- */
- const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
- if (update_loop_mdisps) {
- float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
- BMLoop *l;
-
- BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
- BM_face_calc_center_median(l->f, faces_center[j]);
- }
-
- BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
- BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
- float f_copy_center[3];
- BMIter liter_other;
- BMLoop *l_other;
- int j_other;
-
- BM_face_calc_center_median(f_copy, f_copy_center);
-
- BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) {
- BM_face_interp_multires_ex(bm,
- l_other->f,
- f_copy,
- faces_center[j_other],
- f_copy_center,
- tcld->cd_loop_mdisp_offset);
- }
- }
- }
-}
-
-static void mesh_customdatacorrect_apply(TransInfo *t, bool is_final)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (!tc->custom.type.data) {
- continue;
- }
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
- const bool use_merge_group = tcld->use_merge_group;
-
- struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
- TransData *tob = tc->data;
- for (int i = tc->data_len; i--; tob++) {
- mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
-
- if (use_merge_group) {
- merge_data++;
- }
- }
TransDataMirror *td_mirror = tc->data_mirror;
- for (int i = tc->data_mirror_len; i--; td_mirror++) {
- mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
-
- if (use_merge_group) {
- merge_data++;
+ for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
+ copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
+ if (td_mirror->flag & TD_MIRROR_X) {
+ td_mirror->loc[0] *= -1;
}
- }
- }
-}
-
-static void mesh_customdatacorrect_restore(struct TransInfo *t)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
- if (!tcld) {
- continue;
- }
-
- BMesh *bm = tcld->bm;
- BMesh *bm_copy = tcld->bm_origfaces;
-
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tcld->origfaces) {
- BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
- BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter);
- BLI_assert(f->len == f_copy->len);
-
- BMLoop *l_iter, *l_first, *l_copy;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- l_copy = BM_FACE_FIRST_LOOP(f_copy);
- do {
- /* TODO: Restore only the elements that transform. */
- BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter);
- l_copy = l_copy->next;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Recalc Mesh Data
- * \{ */
-
-static void mesh_apply_to_mirror(TransInfo *t)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (tc->use_mirror_axis_any) {
- int i;
- TransData *td;
- for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
- if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) {
- if (td->flag & TD_MIRROR_EDGE_X) {
- td->loc[0] = 0.0f;
- }
- if (td->flag & TD_MIRROR_EDGE_Y) {
- td->loc[1] = 0.0f;
- }
- if (td->flag & TD_MIRROR_EDGE_Z) {
- td->loc[2] = 0.0f;
- }
- }
+ if (td_mirror->flag & TD_MIRROR_Y) {
+ td_mirror->loc[1] *= -1;
}
-
- TransDataMirror *td_mirror = tc->data_mirror;
- for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
- copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
- if (td_mirror->flag & TD_MIRROR_X) {
- td_mirror->loc[0] *= -1;
- }
- if (td_mirror->flag & TD_MIRROR_Y) {
- td_mirror->loc[1] *= -1;
- }
- if (td_mirror->flag & TD_MIRROR_Z) {
- td_mirror->loc[2] *= -1;
- }
+ if (td_mirror->flag & TD_MIRROR_Z) {
+ td_mirror->loc[2] *= -1;
}
}
}
@@ -1623,27 +1811,45 @@ static void mesh_apply_to_mirror(TransInfo *t)
void recalcData_mesh(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
- /* mirror modifier clipping? */
+ /* Apply corrections. */
if (!is_canceling) {
- /* apply clipping after so we never project past the clip plane T25423. */
applyProject(t);
- clipMirrorModifier(t);
- if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) {
- mesh_apply_to_mirror(t);
- }
+ bool do_mirror = !(t->flag & T_NO_MIRROR);
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ /* Apply clipping after so we never project past the clip plane T25423. */
+ transform_convert_clip_mirror_modifier_apply(tc);
+
+ if (do_mirror) {
+ tc_mesh_transdata_mirror_apply(tc);
+ }
- mesh_customdatacorrect_apply(t, false);
+ tc_mesh_customdatacorrect_apply(tc, false);
+ }
}
else {
- mesh_customdatacorrect_restore(t);
+ tc_mesh_customdatacorrect_restore(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
- EDBM_mesh_normals_update(em);
- BKE_editmesh_looptri_calc(em);
+
+ /* The additional cost of generating the partial connectivity data isn't justified
+ * when all data needs to be updated.
+ *
+ * While proportional editing can cause all geometry to need updating with a partial selection.
+ * It's impractical to calculate this ahead of time.
+ * Further, the down side of using partial updates when their not needed is negligible. */
+ if (em->bm->totvert == em->bm->totvertsel) {
+ EDBM_mesh_normals_update(em);
+ BKE_editmesh_looptri_calc(em);
+ }
+ else {
+ BMPartialUpdate *partial_update_cache = tc_mesh_ensure_partial_update(t, tc);
+ BM_mesh_normals_update_with_partial(em->bm, partial_update_cache);
+ BKE_editmesh_looptri_calc_with_partial(em, partial_update_cache);
+ }
}
}
/** \} */
@@ -1660,7 +1866,9 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
if (!is_canceling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
/* NOTE(joeedh): Handle multi-res re-projection,
* done on transform completion since it's really slow. */
- mesh_customdatacorrect_apply(t, true);
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ tc_mesh_customdatacorrect_apply(tc, true);
+ }
}
if (use_automerge) {
diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c
index bb9296b4b90..3b1191a3401 100644
--- a/source/blender/editors/transform/transform_convert_mesh_edge.c
+++ b/source/blender/editors/transform/transform_convert_mesh_edge.c
@@ -123,4 +123,11 @@ void createTransEdge(TransInfo *t)
}
}
+void recalcData_mesh_edge(TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
+ }
+}
+
/** \} */
diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c
index 5dbb1947773..7c61da31f72 100644
--- a/source/blender/editors/transform/transform_convert_mesh_skin.c
+++ b/source/blender/editors/transform/transform_convert_mesh_skin.c
@@ -47,9 +47,9 @@
/** \name Edit Mesh #CD_MVERT_SKIN Transform Creation
* \{ */
-static float *mesh_skin_transdata_center(const struct TransIslandData *island_data,
- const int island_index,
- BMVert *eve)
+static float *tc_mesh_skin_transdata_center(const struct TransIslandData *island_data,
+ const int island_index,
+ BMVert *eve)
{
if (island_data->center && island_index != -1) {
return island_data->center[island_index];
@@ -57,11 +57,11 @@ static float *mesh_skin_transdata_center(const struct TransIslandData *island_da
return eve->co;
}
-static void mesh_skin_transdata_create(TransDataBasic *td,
- BMEditMesh *em,
- BMVert *eve,
- const struct TransIslandData *island_data,
- const int island_index)
+static void tc_mesh_skin_transdata_create(TransDataBasic *td,
+ BMEditMesh *em,
+ BMVert *eve,
+ const struct TransIslandData *island_data,
+ const int island_index)
{
BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0);
MVertSkin *vs = CustomData_bmesh_get(&em->bm->vdata, eve->head.data, CD_MVERT_SKIN);
@@ -78,7 +78,7 @@ static void mesh_skin_transdata_create(TransDataBasic *td,
td->flag |= TD_SELECTED;
}
- copy_v3_v3(td->center, mesh_skin_transdata_center(island_data, island_index, eve));
+ copy_v3_v3(td->center, tc_mesh_skin_transdata_center(island_data, island_index, eve));
td->extra = eve;
}
@@ -209,7 +209,7 @@ void createTransMeshSkin(TransInfo *t)
}
if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
- mesh_skin_transdata_create(
+ tc_mesh_skin_transdata_create(
(TransDataBasic *)td_mirror, em, eve, &island_data, island_index);
int elem_index = mirror_data.vert_map[a].index;
@@ -221,7 +221,7 @@ void createTransMeshSkin(TransInfo *t)
td_mirror++;
}
else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index);
+ tc_mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index);
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
createSpaceNormal(td->axismtx, eve->no);
@@ -275,7 +275,7 @@ void createTransMeshSkin(TransInfo *t)
/** \name Recalc Mesh Data
* \{ */
-static void mesh_skin_apply_to_mirror(TransInfo *t)
+static void tc_mesh_skin_apply_to_mirror(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->use_mirror_axis_any) {
@@ -292,13 +292,13 @@ void recalcData_mesh_skin(TransInfo *t)
bool is_canceling = t->state == TRANS_CANCEL;
/* mirror modifier clipping? */
if (!is_canceling) {
- if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) {
- mesh_skin_apply_to_mirror(t);
+ if (!(t->flag & T_NO_MIRROR)) {
+ tc_mesh_skin_apply_to_mirror(t);
}
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
EDBM_mesh_normals_update(em);
BKE_editmesh_looptri_calc(em);
diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c
index a5f90e9ac5f..d91a2a8be4b 100644
--- a/source/blender/editors/transform/transform_convert_mesh_uv.c
+++ b/source/blender/editors/transform/transform_convert_mesh_uv.c
@@ -475,7 +475,7 @@ void recalcData_uv(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->data_len) {
- DEG_id_tag_update(tc->obedit->data, 0);
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index b546f1d0b4c..c217478bd04 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -282,9 +282,17 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
*/
BKE_object_to_mat3(ob, obmtx);
copy_m3_m4(totmat, ob->obmat);
- invert_m3_m3(obinv, totmat);
+
+ /* If the object scale is zero on any axis, this might result in a zero matrix.
+ * In this case, the transformation would not do anything, see: T50103. */
+ orthogonalize_m3_zero_axes(obmtx, 1.0f);
+ orthogonalize_m3_zero_axes(totmat, 1.0f);
+
+ /* Use safe invert even though the input matrices have had zero axes set to unit length,
+ * in the unlikely case of failure (float precision for eg) this uses unit matrix fallback. */
+ invert_m3_m3_safe_ortho(obinv, totmat);
mul_m3_m3m3(td->smtx, obmtx, obinv);
- invert_m3_m3(td->mtx, td->smtx);
+ invert_m3_m3_safe_ortho(td->mtx, td->smtx);
}
else {
/* no conversion to/from dataspace */
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index 30418471d6d..6a09008e657 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -90,8 +90,8 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c
Scene *scene = t->scene;
int cfra = CFRA;
- int left = SEQ_transform_get_left_handle_frame(seq, false);
- int right = SEQ_transform_get_right_handle_frame(seq, false);
+ int left = SEQ_transform_get_left_handle_frame(seq);
+ int right = SEQ_transform_get_right_handle_frame(seq);
if (seq->depth == 0 && ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK))) {
*r_recursive = false;
@@ -172,7 +172,7 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c
}
}
-static int SeqTransCount(TransInfo *t, Sequence *parent, ListBase *seqbase, int depth)
+static int SeqTransCount(TransInfo *t, ListBase *seqbase, int depth)
{
Sequence *seq;
int tot = 0, recursive, count, flag;
@@ -180,16 +180,11 @@ static int SeqTransCount(TransInfo *t, Sequence *parent, ListBase *seqbase, int
for (seq = seqbase->first; seq; seq = seq->next) {
seq->depth = depth;
- /* 'seq->tmp' is used by seq_tx_get_final_{left, right}
- * to check sequence's range and clamp to it if needed.
- * It's first place where digging into sequences tree, so store link to parent here. */
- seq->tmp = parent;
-
SeqTransInfo(t, seq, &recursive, &count, &flag); /* ignore the flag */
tot += count;
if (recursive) {
- tot += SeqTransCount(t, seq, &seq->seqbase, depth + 1);
+ tot += SeqTransCount(t, &seq->seqbase, depth + 1);
}
}
@@ -206,16 +201,16 @@ static TransData *SeqToTransData(
/* Use seq_tx_get_final_left() and an offset here
* so transform has the left hand location of the strip.
* tdsq->start_offset is used when flushing the tx data back */
- start_left = SEQ_transform_get_left_handle_frame(seq, false);
+ start_left = SEQ_transform_get_left_handle_frame(seq);
td2d->loc[0] = start_left;
tdsq->start_offset = start_left - seq->start; /* use to apply the original location */
break;
case SEQ_LEFTSEL:
- start_left = SEQ_transform_get_left_handle_frame(seq, false);
+ start_left = SEQ_transform_get_left_handle_frame(seq);
td2d->loc[0] = start_left;
break;
case SEQ_RIGHTSEL:
- td2d->loc[0] = SEQ_transform_get_right_handle_frame(seq, false);
+ td2d->loc[0] = SEQ_transform_get_right_handle_frame(seq);
break;
}
@@ -491,7 +486,7 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c
}
}
- SEQ_sort(t->scene);
+ SEQ_sort(seqbasep);
}
else {
/* Canceled, need to update the strips display */
@@ -562,7 +557,7 @@ void createTransSeqData(TransInfo *t)
}
#endif
- count = SeqTransCount(t, NULL, ed->seqbasep, 0);
+ count = SeqTransCount(t, ed->seqbasep, 0);
/* allocate memory for data */
tc->data_len = count;
@@ -707,7 +702,7 @@ static void flushTransSeq(TransInfo *t)
/* originally TFM_TIME_EXTEND, transform changes */
if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) {
- /* Special annoying case here, need to calc metas with TFM_TIME_EXTEND only */
+ /* Special annoying case here, need to calc meta-strips with TFM_TIME_EXTEND only */
/* calc all meta's then effects T27953. */
for (seq = seqbasep->first; seq; seq = seq->next) {
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index e43a3ff3635..71c91221fbb 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -409,11 +409,10 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
short orient_types[3];
float custom_matrix[3][3];
- short orient_type_scene = V3D_ORIENT_GLOBAL;
- short orient_type_set = V3D_ORIENT_GLOBAL;
- short orient_type_matrix_set = -1;
-
- bool use_orient_axis = false;
+ int orient_type_scene = V3D_ORIENT_GLOBAL;
+ int orient_type_default = -1;
+ int orient_type_set = -1;
+ int orient_type_matrix_set = -1;
if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT];
@@ -424,40 +423,20 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
}
- short orient_type_default = orient_type_scene;
-
- if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis"))) {
- t->orient_axis = RNA_property_enum_get(op->ptr, prop);
- use_orient_axis = true;
- }
-
- if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) {
- t->orient_axis_ortho = RNA_property_enum_get(op->ptr, prop);
- }
-
if (op && ((prop = RNA_struct_find_property(op->ptr, "orient_type")) &&
RNA_property_is_set(op->ptr, prop))) {
orient_type_set = RNA_property_enum_get(op->ptr, prop);
if (orient_type_set >= V3D_ORIENT_CUSTOM + BIF_countTransformOrientation(C)) {
orient_type_set = V3D_ORIENT_GLOBAL;
}
-
- /* Change the default orientation to be used when redoing. */
- orient_type_default = orient_type_set;
}
- else if (t->con.mode & CON_APPLY) {
- orient_type_set = orient_type_scene;
+
+ if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis"))) {
+ t->orient_axis = RNA_property_enum_get(op->ptr, prop);
}
- else {
- if (orient_type_set == orient_type_scene) {
- BLI_assert(orient_type_set == V3D_ORIENT_GLOBAL);
- orient_type_set = V3D_ORIENT_LOCAL;
- }
- if ((t->flag & T_MODAL) && (use_orient_axis || transform_mode_is_changeable(t->mode)) &&
- (t->mode != TFM_ALIGN)) {
- orient_type_default = V3D_ORIENT_VIEW;
- }
+ if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) {
+ t->orient_axis_ortho = RNA_property_enum_get(op->ptr, prop);
}
if (op && ((prop = RNA_struct_find_property(op->ptr, "orient_matrix")) &&
@@ -468,19 +447,41 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
RNA_property_is_set(op->ptr, prop)) {
orient_type_matrix_set = RNA_property_enum_get(op->ptr, prop);
}
- else {
- orient_type_matrix_set = orient_type_set;
+ else if (orient_type_set == -1) {
+ orient_type_set = V3D_ORIENT_CUSTOM_MATRIX;
}
+ }
- if (orient_type_matrix_set == orient_type_set) {
- /* Constraints are forced to use the custom matrix when redoing. */
- orient_type_set = V3D_ORIENT_CUSTOM_MATRIX;
+ if (orient_type_set != -1) {
+ orient_type_default = orient_type_set;
+ t->is_orient_set = true;
+ }
+ else if (orient_type_matrix_set != -1) {
+ orient_type_default = orient_type_set = orient_type_matrix_set;
+ t->is_orient_set = true;
+ }
+ else if (t->con.mode & CON_APPLY) {
+ orient_type_default = orient_type_set = orient_type_scene;
+ }
+ else {
+ orient_type_default = orient_type_scene;
+ if (orient_type_scene == V3D_ORIENT_GLOBAL) {
+ orient_type_set = V3D_ORIENT_LOCAL;
}
+ else {
+ orient_type_set = V3D_ORIENT_GLOBAL;
+ }
+ }
+
+ BLI_assert(!ELEM(-1, orient_type_default, orient_type_set));
+ if (orient_type_matrix_set == orient_type_set) {
+ /* Constraints are forced to use the custom matrix when redoing. */
+ orient_type_set = V3D_ORIENT_CUSTOM_MATRIX;
}
- orient_types[0] = orient_type_default;
- orient_types[1] = orient_type_scene;
- orient_types[2] = orient_type_set;
+ orient_types[O_DEFAULT] = (short)orient_type_default;
+ orient_types[O_SCENE] = (short)orient_type_scene;
+ orient_types[O_SET] = (short)orient_type_set;
for (int i = 0; i < 3; i++) {
/* For efficiency, avoid calculating the same orientation twice. */
@@ -497,9 +498,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
}
- /* Set orient_curr to -1 in order to force the update in
- * `transform_orientations_current_set`. */
- t->orient_curr = -1;
transform_orientations_current_set(t, (t->con.mode & CON_APPLY) ? 2 : 0);
}
@@ -529,7 +527,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else {
/* Avoid mirroring for unsupported contexts. */
- t->options |= CTX_NO_MIRROR;
+ t->flag |= T_NO_MIRROR;
}
/* setting PET flag only if property exist in operator. Otherwise, assume it's not supported */
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 27df29afd8d..7a780df0def 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -512,11 +512,11 @@ static void protectflag_to_drawflags(short protectflag, short *drawflags)
/* for pose mode */
static void protectflag_to_drawflags_pchan(RegionView3D *rv3d,
const bPoseChannel *pchan,
- short orientation_type)
+ short orientation_index)
{
/* Protect-flags apply to local space in pose mode, so only let them influence axis
* visibility if we show the global orientation, otherwise it's confusing. */
- if (orientation_type == V3D_ORIENT_LOCAL) {
+ if (orientation_index == V3D_ORIENT_LOCAL) {
protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
}
}
@@ -657,12 +657,9 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
int a, totsel = 0;
const int pivot_point = scene->toolsettings->transform_pivot_point;
- const short orientation_type = params->orientation_type ?
- (params->orientation_type - 1) :
- scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
- const short orientation_index_custom =
- params->orientation_type ? params->orientation_index_custom :
- scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom;
+ const short orient_index = params->orientation_index ?
+ (params->orientation_index - 1) :
+ BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
/* transform widget matrix */
unit_m4(rv3d->twmat);
@@ -678,7 +675,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
if (ob) {
float mat[3][3];
ED_transform_calc_orientation_from_type_ex(
- C, mat, scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point);
+ C, mat, scene, rv3d, ob, obedit, orient_index, pivot_point);
copy_m4_m3(rv3d->twmat, mat);
}
@@ -976,7 +973,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
Bone *bone = pchan->bone;
if (bone && (bone->flag & BONE_TRANSFORM)) {
calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local);
- protectflag_to_drawflags_pchan(rv3d, pchan, orientation_type);
+ protectflag_to_drawflags_pchan(rv3d, pchan, orient_index);
}
}
totsel += totsel_iter;
@@ -1063,7 +1060,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
/* Protect-flags apply to world space in object mode, so only let them influence axis
* visibility if we show the global orientation, otherwise it's confusing. */
- if (orientation_type == V3D_ORIENT_GLOBAL) {
+ if (orient_index == V3D_ORIENT_GLOBAL) {
protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
}
totsel++;
@@ -1689,18 +1686,15 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
}
}
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
- scene, ggd->twtype_init);
+ const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, ggd->twtype_init);
/* skip, we don't draw anything anyway */
- if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(
- C,
- &(struct TransformCalcParams){
- .use_only_center = true,
- .orientation_type = orient_slot->type + 1,
- .orientation_index_custom = orient_slot->index_custom,
- },
- &tbounds) == 0))) {
+ if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C,
+ &(struct TransformCalcParams){
+ .use_only_center = true,
+ .orientation_index = orient_index + 1,
+ },
+ &tbounds) == 0))) {
return;
}
@@ -2119,14 +2113,12 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr
gzgroup->use_fallback_keymap = false;
}
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene,
- SCE_ORIENT_SCALE);
+ const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, SCE_ORIENT_SCALE);
if ((ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.use_local_axis = true,
- .orientation_type = orient_slot->type + 1,
- .orientation_index_custom = orient_slot->index_custom,
+ .orientation_index = orient_index + 1,
},
&tbounds) == 0) ||
equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max)) {
@@ -2335,14 +2327,14 @@ static void WIDGETGROUP_xform_shear_refresh(const bContext *C, wmGizmoGroup *gzg
/* Needed to test view orientation changes. */
copy_m3_m4(xgzgroup->prev.viewinv_m3, rv3d->viewinv);
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene,
- SCE_ORIENT_ROTATE);
+ TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
+ scene, SCE_ORIENT_ROTATE);
+ const int orient_index = BKE_scene_orientation_slot_get_index(orient_slot);
if (ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.use_local_axis = false,
- .orientation_type = orient_slot->type + 1,
- .orientation_index_custom = orient_slot->index_custom,
+ .orientation_index = orient_index + 1,
},
&tbounds) == 0) {
for (int i = 0; i < 3; i++) {
diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
index 63c8efdd475..ed6c3eb0255 100644
--- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
@@ -25,6 +25,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_scene.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -88,7 +89,7 @@ typedef struct GizmoExtrudeGroup {
struct {
float normal_mat3[3][3]; /* use Z axis for normal. */
- int orientation_type;
+ int orientation_index;
} data;
wmOperatorType *ot_extrude;
@@ -254,8 +255,8 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
axis_type = RNA_property_enum_get(&ptr, ggd->gzgt_axis_type_prop);
}
- ggd->data.orientation_type = scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
- const bool use_normal = ((ggd->data.orientation_type != V3D_ORIENT_NORMAL) ||
+ ggd->data.orientation_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
+ const bool use_normal = ((ggd->data.orientation_index != V3D_ORIENT_NORMAL) ||
(axis_type == EXTRUDE_AXIS_NORMAL));
const int axis_len_used = use_normal ? 4 : 3;
@@ -265,7 +266,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
struct TransformBounds tbounds_normal;
if (!ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
- .orientation_type = V3D_ORIENT_NORMAL + 1,
+ .orientation_index = V3D_ORIENT_NORMAL + 1,
},
&tbounds_normal)) {
unit_m3(tbounds_normal.axis);
@@ -276,7 +277,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
/* TODO(campbell): run second since this modifies the 3D view, it should not. */
if (!ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
- .orientation_type = ggd->data.orientation_type + 1,
+ .orientation_index = ggd->data.orientation_index + 1,
},
&tbounds)) {
return;
@@ -391,7 +392,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
static void gizmo_mesh_extrude_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
{
GizmoExtrudeGroup *ggd = gzgroup->customdata;
- switch (ggd->data.orientation_type) {
+ switch (ggd->data.orientation_index) {
case V3D_ORIENT_VIEW: {
RegionView3D *rv3d = CTX_wm_region_view3d(C);
float mat[3][3];
@@ -451,7 +452,7 @@ static void gizmo_mesh_extrude_invoke_prepare(const bContext *UNUSED(C),
if (i == 3) {
use_normal_matrix = true;
}
- else if (ggd->data.orientation_type == V3D_ORIENT_NORMAL) {
+ else if (ggd->data.orientation_index == V3D_ORIENT_NORMAL) {
use_normal_matrix = true;
}
if (use_normal_matrix) {
diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c
index bfeb96d18c4..414199badd7 100644
--- a/source/blender/editors/transform/transform_input.c
+++ b/source/blender/editors/transform/transform_input.c
@@ -186,57 +186,24 @@ struct InputAngle_Data {
static void InputAngle(TransInfo *UNUSED(t), MouseInput *mi, const double mval[2], float output[3])
{
struct InputAngle_Data *data = mi->data;
- double dx2 = mval[0] - (double)mi->center[0];
- double dy2 = mval[1] - (double)mi->center[1];
- double B = sqrt(dx2 * dx2 + dy2 * dy2);
+ float dir_prev[2], dir_curr[2], mi_center[2];
+ copy_v2_v2(mi_center, mi->center);
- double dx1 = data->mval_prev[0] - (double)mi->center[0];
- double dy1 = data->mval_prev[1] - (double)mi->center[1];
- double A = sqrt(dx1 * dx1 + dy1 * dy1);
+ sub_v2_v2v2(dir_prev, (const float[2]){UNPACK2(data->mval_prev)}, mi_center);
+ sub_v2_v2v2(dir_curr, (const float[2]){UNPACK2(mval)}, mi_center);
- double dx3 = mval[0] - data->mval_prev[0];
- double dy3 = mval[1] - data->mval_prev[1];
+ if (normalize_v2(dir_prev) && normalize_v2(dir_curr)) {
+ float dphi = angle_normalized_v2v2(dir_prev, dir_curr);
- /* use doubles here, to make sure a "1.0" (no rotation)
- * doesn't become 9.999999e-01, which gives 0.02 for acos */
- double deler = (((dx1 * dx1 + dy1 * dy1) + (dx2 * dx2 + dy2 * dy2) - (dx3 * dx3 + dy3 * dy3)) /
- (2.0 * (((A * B) != 0.0) ? (A * B) : 1.0)));
- /* ((A * B) ? (A * B) : 1.0) this takes care of potential divide by zero errors */
-
- float dphi;
-
- dphi = saacos((float)deler);
- if ((dx1 * dy2 - dx2 * dy1) > 0.0) {
- dphi = -dphi;
- }
-
- /* If the angle is zero, because of lack of precision close to the 1.0 value in acos
- * approximate the angle with the opposite side of the normalized triangle
- * This is a good approximation here since the smallest acos value seems to be around
- * 0.02 degree and lower values don't even have a 0.01% error compared to the approximation
- */
- if (dphi == 0) {
- double dx, dy;
-
- dx2 /= A;
- dy2 /= A;
-
- dx1 /= B;
- dy1 /= B;
-
- dx = dx1 - dx2;
- dy = dy1 - dy2;
-
- dphi = sqrt(dx * dx + dy * dy);
- if ((dx1 * dy2 - dx2 * dy1) > 0.0) {
+ if (cross_v2v2(dir_prev, dir_curr) > 0.0f) {
dphi = -dphi;
}
- }
- data->angle += ((double)dphi) * (mi->precision ? (double)mi->precision_factor : 1.0);
+ data->angle += ((double)dphi) * (mi->precision ? (double)mi->precision_factor : 1.0);
- data->mval_prev[0] = mval[0];
- data->mval_prev[1] = mval[1];
+ data->mval_prev[0] = mval[0];
+ data->mval_prev[1] = mval[1];
+ }
output[0] = data->angle;
}
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index d14f693da9c..350be247014 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -45,6 +45,7 @@
#include "transform.h"
#include "transform_convert.h"
+#include "transform_orientations.h"
#include "transform_snap.h"
/* Own include. */
@@ -523,7 +524,7 @@ void constraintSizeLim(TransInfo *t, TransData *td)
/** \name Transform (Rotation Utils)
* \{ */
/* Used by Transform Rotation and Transform Normal Rotation */
-void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final)
+void headerRotation(TransInfo *t, char *str, const int str_size, float final)
{
size_t ofs = 0;
@@ -532,25 +533,21 @@ void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final)
outputNumInput(&(t->num), c, &t->scene->unit);
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Rotation: %s %s %s"),
- &c[0],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, str_size - ofs, TIP_("Rotation: %s %s %s"), &c[0], t->con.text, t->proptext);
}
else {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Rotation: %.2f%s %s"),
- RAD2DEGF(final),
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Rotation: %.2f%s %s"),
+ RAD2DEGF(final),
+ t->con.text,
+ t->proptext);
}
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
- str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
}
@@ -810,7 +807,7 @@ void ElementRotation(
/* -------------------------------------------------------------------- */
/** \name Transform (Resize Utils)
* \{ */
-void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR])
+void headerResize(TransInfo *t, const float vec[3], char *str, const int str_size)
{
char tvec[NUM_STR_REP_LEN * 3];
size_t ofs = 0;
@@ -826,59 +823,55 @@ void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR])
if (t->con.mode & CON_APPLY) {
switch (t->num.idx_max) {
case 0:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale: %s%s %s"),
- &tvec[0],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, str_size - ofs, TIP_("Scale: %s%s %s"), &tvec[0], t->con.text, t->proptext);
break;
case 1:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale: %s : %s%s %s"),
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Scale: %s : %s%s %s"),
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ t->con.text,
+ t->proptext);
break;
case 2:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale: %s : %s : %s%s %s"),
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- &tvec[NUM_STR_REP_LEN * 2],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Scale: %s : %s : %s%s %s"),
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ &tvec[NUM_STR_REP_LEN * 2],
+ t->con.text,
+ t->proptext);
break;
}
}
else {
if (t->flag & T_2D_EDIT) {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale X: %s Y: %s%s %s"),
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Scale X: %s Y: %s%s %s"),
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ t->con.text,
+ t->proptext);
}
else {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale X: %s Y: %s Z: %s%s %s"),
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- &tvec[NUM_STR_REP_LEN * 2],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Scale X: %s Y: %s Z: %s%s %s"),
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ &tvec[NUM_STR_REP_LEN * 2],
+ t->con.text,
+ t->proptext);
}
}
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
- str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
}
@@ -1263,7 +1256,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
if (t->data_type == TC_MESH_VERTS) {
/* Init Custom Data correction.
* Ideally this should be called when creating the TransData. */
- mesh_customdatacorrect_init(t);
+ transform_convert_mesh_customdatacorrect_init(t);
}
/* TODO(germano): Some of these operations change the `t->mode`.
@@ -1271,4 +1264,38 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
* BLI_assert(t->mode == mode); */
}
+/**
+ * When in modal and not set, initializes a default orientation for the mode.
+ */
+void transform_mode_default_modal_orientation_set(TransInfo *t, int type)
+{
+ /* Currently only these types are supported. */
+ BLI_assert(ELEM(type, V3D_ORIENT_GLOBAL, V3D_ORIENT_VIEW));
+
+ if (t->is_orient_set) {
+ return;
+ }
+
+ if (!(t->flag & T_MODAL)) {
+ return;
+ }
+
+ if (t->orient[O_DEFAULT].type == type) {
+ return;
+ }
+
+ RegionView3D *rv3d = NULL;
+ if ((type == V3D_ORIENT_VIEW) && (t->spacetype == SPACE_VIEW3D) && t->region &&
+ (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ rv3d = t->region->regiondata;
+ }
+
+ t->orient[O_DEFAULT].type = ED_transform_calc_orientation_from_type_ex(
+ NULL, t->orient[O_DEFAULT].matrix, NULL, rv3d, NULL, NULL, type, 0);
+
+ if (t->orient_curr == O_DEFAULT) {
+ /* Update Orientation. */
+ transform_orientations_current_set(t, O_DEFAULT);
+ }
+}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h
index 6d7a0b528ae..a2b95eb3de4 100644
--- a/source/blender/editors/transform/transform_mode.h
+++ b/source/blender/editors/transform/transform_mode.h
@@ -47,7 +47,7 @@ void protectedTransBits(short protectflag, float vec[3]);
void protectedSizeBits(short protectflag, float size[3]);
void constraintTransLim(TransInfo *t, TransData *td);
void constraintSizeLim(TransInfo *t, TransData *td);
-void headerRotation(TransInfo *t, char *str, float final);
+void headerRotation(TransInfo *t, char *str, int str_size, float final);
void ElementRotation_ex(TransInfo *t,
TransDataContainer *tc,
TransData *td,
@@ -55,12 +55,13 @@ void ElementRotation_ex(TransInfo *t,
const float *center);
void ElementRotation(
TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around);
-void headerResize(TransInfo *t, const float vec[3], char *str);
+void headerResize(TransInfo *t, const float vec[3], char *str, int str_size);
void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]);
short getAnimEdit_SnapMode(TransInfo *t);
void doAnimEdit_SnapFrame(
TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap);
void transform_mode_init(TransInfo *t, struct wmOperator *op, const int mode);
+void transform_mode_default_modal_orientation_set(TransInfo *t, int type);
/* transform_mode_align.c */
void initAlign(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
index ee63bf4be6f..68416c780ef 100644
--- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
@@ -107,7 +107,6 @@ void initCurveShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
index c78115561b2..6f2bcc148ce 100644
--- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
+++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
@@ -103,7 +103,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2]))
applyNumInput(&t->num, &angle);
- headerRotation(t, str, angle);
+ headerRotation(t, str, sizeof(str), angle);
axis_angle_normalized_to_mat3(mat, axis, angle);
@@ -148,5 +148,7 @@ void initNormalRotation(TransInfo *t)
storeCustomLNorValue(tc, bm);
}
+
+ transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
index 4330d5e79be..7e7b79c9f90 100644
--- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
@@ -72,7 +72,7 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.0f, %.0f", val[0], val[1]);
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text);
const wmKeyMapItem *kmi = t->custom.mode.data;
@@ -80,10 +80,10 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA
ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs);
}
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_(" or Alt) Expand to fit %s"),
- WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0));
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ TIP_(" or Alt) Expand to fit %s"),
+ WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0));
}
static void applySeqSlideValue(TransInfo *t, const float val[2])
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index 16c1c05a6f8..d255a7d5660 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -1482,15 +1482,15 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs);
}
else {
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final);
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even));
if (use_even) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped));
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp));
/* done with header string */
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index e67c6c03481..7c496d271ef 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -117,7 +117,6 @@ void initGPOpacity(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index 95e3d89d2b7..608a49f38b1 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -119,7 +119,6 @@ void initGPShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
index 3019984d70b..cfbd6030788 100644
--- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
@@ -89,7 +89,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
/* apply shrink/fatten */
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- TransData *td = tc->data;
+ TransData *td;
for (td = tc->data, i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
@@ -133,7 +133,6 @@ void initMaskShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c
index 9891af8b9a4..f225f1a7c06 100644
--- a/source/blender/editors/transform/transform_mode_mirror.c
+++ b/source/blender/editors/transform/transform_mode_mirror.c
@@ -235,8 +235,5 @@ void initMirror(TransInfo *t)
initMouseInputMode(t, &t->mouse, INPUT_NONE);
t->flag |= T_NULL_ONE;
- if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
- }
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index 4d0bb7fbe9c..1d7d1369f29 100644
--- a/source/blender/editors/transform/transform_mode_resize.c
+++ b/source/blender/editors/transform/transform_mode_resize.c
@@ -113,10 +113,10 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
pvec[j++] = t->values_final[i];
}
}
- headerResize(t, pvec, str);
+ headerResize(t, pvec, str, sizeof(str));
}
else {
- headerResize(t, t->values_final, str);
+ headerResize(t, t->values_final, str, sizeof(str));
}
copy_m3_m3(t->mat, mat); /* used in gizmo */
@@ -176,7 +176,6 @@ void initResize(TransInfo *t)
t->num.val_flag[2] |= NUM_NULL_ONE;
t->num.flag |= NUM_AFFECT_ALL;
if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
t->num.val_flag[1] |= NUM_NO_ZERO;
@@ -194,5 +193,7 @@ void initResize(TransInfo *t)
t->num.unit_type[0] = B_UNIT_NONE;
t->num.unit_type[1] = B_UNIT_NONE;
t->num.unit_type[2] = B_UNIT_NONE;
+
+ transform_mode_default_modal_orientation_set(t, V3D_ORIENT_GLOBAL);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index 8d8594d5775..8350e94e0e8 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -217,7 +217,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
t->values_final[0] = final;
- headerRotation(t, str, final);
+ headerRotation(t, str, sizeof(str), final);
const bool is_large_rotation = hasNumInput(&t->num);
applyRotationValue(t, final, axis_final, is_large_rotation);
@@ -249,5 +249,7 @@ void initRotation(TransInfo *t)
if (t->flag & T_2D_EDIT) {
t->flag |= T_NO_CONSTRAINT;
}
+
+ transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c
index a41c49710b9..23ee55bf6c5 100644
--- a/source/blender/editors/transform/transform_mode_shear.c
+++ b/source/blender/editors/transform/transform_mode_shear.c
@@ -232,5 +232,7 @@ void initShear(TransInfo *t)
t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */
t->flag |= T_NO_CONSTRAINT;
+
+ transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c
index 6e497d85417..d2d73a14396 100644
--- a/source/blender/editors/transform/transform_mode_shrink_fatten.c
+++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c
@@ -79,7 +79,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
if (hasNumInput(&t->num)) {
char c[NUM_STR_REP_LEN];
outputNumInput(&(t->num), c, unit);
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%s", c);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%s", c);
}
else {
/* default header print */
@@ -93,12 +93,12 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
true);
}
else {
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f", distance);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f", distance);
}
}
if (t->proptext[0]) {
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %s", t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, " %s", t->proptext);
}
ofs += BLI_strncpy_rlen(str + ofs, ", (", sizeof(str) - ofs);
diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c
index 8beacb844b9..75ad83b0787 100644
--- a/source/blender/editors/transform/transform_mode_skin_resize.c
+++ b/source/blender/editors/transform/transform_mode_skin_resize.c
@@ -64,7 +64,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2]))
size_to_mat3(mat, t->values_final);
- headerResize(t, t->values_final, str);
+ headerResize(t, t->values_final, str, sizeof(str));
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
@@ -111,7 +111,6 @@ void initSkinResize(TransInfo *t)
t->num.val_flag[2] |= NUM_NULL_ONE;
t->num.flag |= NUM_AFFECT_ALL;
if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
t->num.val_flag[1] |= NUM_NO_ZERO;
diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c
index 5ad6d04b4de..948242e547f 100644
--- a/source/blender/editors/transform/transform_mode_timetranslate.c
+++ b/source/blender/editors/transform/transform_mode_timetranslate.c
@@ -76,10 +76,10 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR])
}
}
- ofs += BLI_snprintf(str, UI_MAX_DRAW_STR, TIP_("DeltaX: %s"), &tvec[0]);
+ ofs += BLI_snprintf_rlen(str, UI_MAX_DRAW_STR, TIP_("DeltaX: %s"), &tvec[0]);
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
}
diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c
index 5a57a69f986..d05077ef1ef 100644
--- a/source/blender/editors/transform/transform_mode_trackball.c
+++ b/source/blender/editors/transform/transform_mode_trackball.c
@@ -102,24 +102,24 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2]))
outputNumInput(&(t->num), c, &t->scene->unit);
- ofs += BLI_snprintf(str + ofs,
- sizeof(str) - ofs,
- TIP_("Trackball: %s %s %s"),
- &c[0],
- &c[NUM_STR_REP_LEN],
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ sizeof(str) - ofs,
+ TIP_("Trackball: %s %s %s"),
+ &c[0],
+ &c[NUM_STR_REP_LEN],
+ t->proptext);
}
else {
- ofs += BLI_snprintf(str + ofs,
- sizeof(str) - ofs,
- TIP_("Trackball: %.2f %.2f %s"),
- RAD2DEGF(phi[0]),
- RAD2DEGF(phi[1]),
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ sizeof(str) - ofs,
+ TIP_("Trackball: %.2f %.2f %s"),
+ RAD2DEGF(phi[0]),
+ RAD2DEGF(phi[1]),
+ t->proptext);
}
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 41fc6ee0aaf..3088f6a7776 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -141,67 +141,67 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
if (t->con.mode & CON_APPLY) {
switch (t->num.idx_max) {
case 0:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "D: %s (%s)%s %s %s",
- &tvec[0],
- distvec,
- t->con.text,
- t->proptext,
- autoik);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "D: %s (%s)%s %s %s",
+ &tvec[0],
+ distvec,
+ t->con.text,
+ t->proptext,
+ autoik);
break;
case 1:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "D: %s D: %s (%s)%s %s %s",
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- distvec,
- t->con.text,
- t->proptext,
- autoik);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "D: %s D: %s (%s)%s %s %s",
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ distvec,
+ t->con.text,
+ t->proptext,
+ autoik);
break;
case 2:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "D: %s D: %s D: %s (%s)%s %s %s",
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- &tvec[NUM_STR_REP_LEN * 2],
- distvec,
- t->con.text,
- t->proptext,
- autoik);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "D: %s D: %s D: %s (%s)%s %s %s",
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ &tvec[NUM_STR_REP_LEN * 2],
+ distvec,
+ t->con.text,
+ t->proptext,
+ autoik);
break;
}
}
else {
if (t->flag & T_2D_EDIT) {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "Dx: %s Dy: %s (%s)%s %s",
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- distvec,
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "Dx: %s Dy: %s (%s)%s %s",
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ distvec,
+ t->con.text,
+ t->proptext);
}
else {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "Dx: %s Dy: %s Dz: %s (%s)%s %s %s",
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- &tvec[NUM_STR_REP_LEN * 2],
- distvec,
- t->con.text,
- t->proptext,
- autoik);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "Dx: %s Dy: %s Dz: %s (%s)%s %s %s",
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ &tvec[NUM_STR_REP_LEN * 2],
+ distvec,
+ t->con.text,
+ t->proptext,
+ autoik);
}
}
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
@@ -217,12 +217,12 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
WM_modalkeymap_items_to_string(
t->keymap, TFM_MODAL_INSERTOFS_TOGGLE_DIR, true, str_km, sizeof(str_km));
- ofs += BLI_snprintf(str,
- UI_MAX_DRAW_STR,
- TIP_("Auto-offset set to %s - press %s to toggle direction | %s"),
- str_dir,
- str_km,
- str_old);
+ ofs += BLI_snprintf_rlen(str,
+ UI_MAX_DRAW_STR,
+ TIP_("Auto-offset set to %s - press %s to toggle direction | %s"),
+ str_dir,
+ str_km,
+ str_old);
MEM_freeN((void *)str_old);
}
@@ -470,5 +470,8 @@ void initTranslation(TransInfo *t)
t->num.unit_type[1] = B_UNIT_NONE;
t->num.unit_type[2] = B_UNIT_NONE;
}
+
+ transform_mode_default_modal_orientation_set(
+ t, (t->options & CTX_CAMERA) ? V3D_ORIENT_VIEW : V3D_ORIENT_GLOBAL);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 1e5d027e253..e16aa636872 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -606,15 +606,15 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs);
}
else {
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final);
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even));
if (use_even) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped));
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp));
/* done with header string */
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index 1470d3b7059..d97bcba161f 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -445,7 +445,7 @@ int BIF_countTransformOrientation(const bContext *C)
return BLI_listbase_count(transform_orientations);
}
-void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char *r_name)
+void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
{
if (r_name) {
BLI_strncpy(r_name, ts->name, MAX_NAME);
@@ -492,12 +492,11 @@ void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3
Object *obedit = CTX_data_edit_object(C);
RegionView3D *rv3d = region->regiondata;
Object *ob = OBACT(view_layer);
- const short orientation_type = scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
- const short orientation_index_custom = scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom;
+ const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
const int pivot_point = scene->toolsettings->transform_pivot_point;
ED_transform_calc_orientation_from_type_ex(
- C, r_mat, scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point);
+ C, r_mat, scene, rv3d, ob, obedit, orient_index, pivot_point);
}
/**
@@ -516,11 +515,10 @@ short ED_transform_calc_orientation_from_type_ex(const bContext *C,
RegionView3D *rv3d,
Object *ob,
Object *obedit,
- const short orientation_type,
- int orientation_index_custom,
+ const short orientation_index,
const int pivot_point)
{
- switch (orientation_type) {
+ switch (orientation_index) {
case V3D_ORIENT_GIMBAL: {
if (ob && gimbal_axis(ob, r_mat)) {
break;
@@ -577,24 +575,28 @@ short ED_transform_calc_orientation_from_type_ex(const bContext *C,
}
case V3D_ORIENT_CUSTOM:
default: {
- BLI_assert(orientation_type >= V3D_ORIENT_CUSTOM);
+ BLI_assert(orientation_index >= V3D_ORIENT_CUSTOM);
+ int orientation_index_custom = orientation_index - V3D_ORIENT_CUSTOM;
TransformOrientation *custom_orientation = BKE_scene_transform_orientation_find(
scene, orientation_index_custom);
applyTransformOrientation(custom_orientation, r_mat, NULL);
- return V3D_ORIENT_CUSTOM + orientation_index_custom;
+ break;
}
}
- return orientation_type;
+ return orientation_index;
}
/* Sets the matrix of the specified space orientation.
* If the matrix cannot be obtained, an orientation different from the one
* informed is returned */
-short transform_orientation_matrix_get(
- bContext *C, TransInfo *t, short orientation, const float custom[3][3], float r_spacemtx[3][3])
+short transform_orientation_matrix_get(bContext *C,
+ TransInfo *t,
+ short orient_index,
+ const float custom[3][3],
+ float r_spacemtx[3][3])
{
- if (orientation == V3D_ORIENT_CUSTOM_MATRIX) {
+ if (orient_index == V3D_ORIENT_CUSTOM_MATRIX) {
copy_m3_m3(r_spacemtx, custom);
return V3D_ORIENT_CUSTOM_MATRIX;
}
@@ -603,24 +605,20 @@ short transform_orientation_matrix_get(
Object *obedit = CTX_data_edit_object(C);
Scene *scene = t->scene;
RegionView3D *rv3d = NULL;
- int orientation_index_custom = 0;
-
- if (orientation >= V3D_ORIENT_CUSTOM) {
- orientation_index_custom = orientation - V3D_ORIENT_CUSTOM;
- orientation = V3D_ORIENT_CUSTOM;
- }
- else if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
- Object *ob_armature = transform_object_deform_pose_armature_get(t, ob);
- if (ob_armature) {
- ob = ob_armature;
- }
- }
if ((t->spacetype == SPACE_VIEW3D) && t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) {
rv3d = t->region->regiondata;
+
+ if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
+ Object *ob_armature = transform_object_deform_pose_armature_get(t, ob);
+ if (ob_armature) {
+ /* The armature matrix is used for GIMBAL, NORMAL and LOCAL orientations. */
+ ob = ob_armature;
+ }
+ }
}
- short orient_type = ED_transform_calc_orientation_from_type_ex(
+ short r_orient_index = ED_transform_calc_orientation_from_type_ex(
C,
r_spacemtx,
/* extra args (can be accessed from context) */
@@ -628,13 +626,12 @@ short transform_orientation_matrix_get(
rv3d,
ob,
obedit,
- orientation,
- orientation_index_custom,
+ orient_index,
t->around);
if (rv3d && (t->options & CTX_PAINT_CURVE)) {
/* Screen space in the 3d region. */
- if (orient_type == V3D_ORIENT_VIEW) {
+ if (r_orient_index == V3D_ORIENT_VIEW) {
unit_m3(r_spacemtx);
}
else {
@@ -643,7 +640,7 @@ short transform_orientation_matrix_get(
}
}
- return orient_type;
+ return r_orient_index;
}
const char *transform_orientations_spacename_get(TransInfo *t, const short orient_type)
@@ -674,10 +671,6 @@ const char *transform_orientations_spacename_get(TransInfo *t, const short orien
void transform_orientations_current_set(TransInfo *t, const short orient_index)
{
- if (t->orient_curr == orient_index) {
- return;
- }
-
const short orientation = t->orient[orient_index].type;
const char *spacename = transform_orientations_spacename_get(t, orientation);
diff --git a/source/blender/editors/transform/transform_orientations.h b/source/blender/editors/transform/transform_orientations.h
index e9c146a6853..de8c9b165c1 100644
--- a/source/blender/editors/transform/transform_orientations.h
+++ b/source/blender/editors/transform/transform_orientations.h
@@ -27,7 +27,7 @@ struct TransInfo;
short transform_orientation_matrix_get(struct bContext *C,
struct TransInfo *t,
- short orientation,
+ short orient_index,
const float custom[3][3],
float r_spacemtx[3][3]);
const char *transform_orientations_spacename_get(struct TransInfo *t, const short orient_type);
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index d0f91802fff..bebef049718 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -345,7 +345,7 @@ void applyProject(TransInfo *t)
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
.use_occlusion_test = false,
.use_backface_culling = t->tsnap.use_backface_culling,
},
@@ -1072,7 +1072,7 @@ static void TargetSnapClosest(TransInfo *t)
if (t->options & CTX_OBJECT) {
int i;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- TransData *td = tc->data;
+ TransData *td;
for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
const BoundBox *bb = NULL;
@@ -1167,7 +1167,7 @@ short snapObjectsTransform(
t->settings->snap_mode,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
.use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE,
.use_backface_culling = t->tsnap.use_backface_culling,
},
@@ -1201,7 +1201,7 @@ bool peelObjectsTransform(TransInfo *t,
t->depsgraph,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
},
mval,
-1.0f,
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 58198f21ba2..512f912a532 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -43,6 +43,8 @@
#include "BKE_curve.h"
#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
+#include "BKE_geometry_set.h"
+#include "BKE_global.h"
#include "BKE_layer.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -144,9 +146,37 @@ struct SnapObjectContext {
/** \name Utilities
* \{ */
-static bool editmesh_eval_final_is_bmesh(const BMEditMesh *em)
+/* Mesh used for snapping.
+ * If NULL the BMesh should be used. */
+static Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide)
{
- return (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH);
+ Mesh *me_eval = ob_eval->data;
+ bool use_hide = false;
+ if (BKE_object_is_in_editmode(ob_eval)) {
+ if (edit_mode_type == SNAP_GEOM_EDIT) {
+ return NULL;
+ }
+
+ BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
+ if ((edit_mode_type == SNAP_GEOM_FINAL) && em_eval->mesh_eval_final) {
+ if (em_eval->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ return NULL;
+ }
+ me_eval = em_eval->mesh_eval_final;
+ use_hide = true;
+ }
+ else if ((edit_mode_type == SNAP_GEOM_CAGE) && em_eval->mesh_eval_cage) {
+ if (em_eval->mesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ return NULL;
+ }
+ me_eval = em_eval->mesh_eval_cage;
+ use_hide = true;
+ }
+ }
+ if (r_use_hide) {
+ *r_use_hide = use_hide;
+ }
+ return me_eval;
}
/** \} */
@@ -208,30 +238,69 @@ static void snap_object_data_clear(SnapObjectData *sod)
memset(&sod->type, 0x0, sizeof(*sod) - offsetof(SnapObjectData, type));
}
-static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob)
+static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob_eval)
{
- SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob);
+ SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval);
if (sod == NULL) {
if (sctx->cache.data_to_object_map != NULL) {
- ob = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob->data);
+ ob_eval = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob_eval->data);
/* Could be NULl when mixing edit-mode and non edit-mode objects. */
- if (ob != NULL) {
- sod = BLI_ghash_lookup(sctx->cache.object_map, ob);
+ if (ob_eval != NULL) {
+ sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval);
}
}
}
return sod;
}
-static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object *ob)
+static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx,
+ Object *ob_eval,
+ Mesh *me_eval,
+ bool use_hide)
{
SnapObjectData *sod;
void **sod_p;
bool init = false;
- if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
+ if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) {
sod = *sod_p;
+ bool is_dirty = false;
if (sod->type != SNAP_MESH) {
+ is_dirty = true;
+ }
+ else if (sod->treedata_mesh.tree && sod->treedata_mesh.cached &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->treedata_mesh.tree)) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (sod->bvhtree[0] && sod->cached[0] &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[0])) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (sod->bvhtree[1] && sod->cached[1] &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[1])) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.looptri_allocated &&
+ sod->treedata_mesh.looptri != me_eval->runtime.looptris.array) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.vert_allocated && sod->treedata_mesh.vert != me_eval->mvert) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.loop_allocated && sod->treedata_mesh.loop != me_eval->mloop) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.edge_allocated && sod->treedata_mesh.edge != me_eval->medge) {
+ is_dirty = true;
+ }
+ else if (sod->poly != me_eval->mpoly) {
+ is_dirty = true;
+ }
+
+ if (is_dirty) {
snap_object_data_clear(sod);
init = true;
}
@@ -243,8 +312,32 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object
if (init) {
sod->type = SNAP_MESH;
- /* start assuming that it has each of these element types */
- sod->has_looptris = true;
+
+ /* The BVHTree from looptris is always required. */
+ BLI_assert(sod->treedata_mesh.tree == NULL);
+ BKE_bvhtree_from_mesh_get(&sod->treedata_mesh,
+ me_eval,
+ use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI,
+ 4);
+
+ if (sod->treedata_mesh.tree == NULL) {
+ sod->treedata_mesh.vert = me_eval->mvert;
+ sod->treedata_mesh.loop = me_eval->mloop;
+ sod->treedata_mesh.looptri = BKE_mesh_runtime_looptri_ensure(me_eval);
+ BLI_assert(sod->has_looptris == false);
+ }
+ else {
+ BLI_assert(sod->treedata_mesh.vert != NULL);
+ BLI_assert(sod->treedata_mesh.loop != NULL);
+ BLI_assert(sod->treedata_mesh.looptri != NULL);
+ sod->has_looptris = true;
+ }
+
+ /* Required for snapping with occlusion. */
+ sod->treedata_mesh.edge = me_eval->medge;
+ sod->poly = me_eval->mpoly;
+
+ /* Start assuming that it has each of these element types. */
sod->has_loose_edge = true;
sod->has_loose_vert = true;
}
@@ -252,26 +345,26 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object
return sod;
}
-static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob)
+static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob_eval)
{
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- return &em->mesh_eval_final->runtime;
+ BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
+ if (em_eval->mesh_eval_final) {
+ return &em_eval->mesh_eval_final->runtime;
}
- if (em->mesh_eval_cage) {
- return &em->mesh_eval_cage->runtime;
+ if (em_eval->mesh_eval_cage) {
+ return &em_eval->mesh_eval_cage->runtime;
}
- return &((Mesh *)ob->data)->runtime;
+ return &((Mesh *)ob_eval->data)->runtime;
}
static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em)
{
SnapObjectData *sod;
void **sod_p;
- bool init = false, init_min_max = true, clear_cache = false;
+ bool init = false;
{
/* Use object-data as the key in ghash since the editmesh
@@ -280,48 +373,53 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
sctx->cache.data_to_object_map = BLI_ghash_ptr_new(__func__);
}
void **ob_p;
- if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob->data, &ob_p)) {
- ob = *ob_p;
+ if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob_eval->data, &ob_p)) {
+ ob_eval = *ob_p;
}
else {
- *ob_p = ob;
+ *ob_p = ob_eval;
}
}
- if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
+ if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) {
sod = *sod_p;
- bool clear = false;
+ bool is_dirty = false;
/* Check if the geometry has changed. */
if (sod->type != SNAP_EDIT_MESH) {
- clear = true;
+ is_dirty = true;
}
else if (sod->treedata_editmesh.em != em) {
- clear_cache = true;
- init = true;
+ is_dirty = true;
}
else if (sod->mesh_runtime) {
- if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob)) {
- clear_cache = true;
- init = true;
+ if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) {
+ if (G.moving) {
+ /* Hack to avoid updating while transforming. */
+ BLI_assert(!sod->treedata_editmesh.cached && !sod->cached[0] && !sod->cached[1]);
+ sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
+ }
+ else {
+ is_dirty = true;
+ }
}
else if (sod->treedata_editmesh.tree && sod->treedata_editmesh.cached &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->treedata_editmesh.tree)) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
else if (sod->bvhtree[0] && sod->cached[0] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[0])) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
else if (sod->bvhtree[1] && sod->cached[1] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[1])) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
}
- if (clear) {
+ if (is_dirty) {
snap_object_data_clear(sod);
init = true;
}
@@ -334,27 +432,8 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
if (init) {
sod->type = SNAP_EDIT_MESH;
sod->treedata_editmesh.em = em;
-
- if (clear_cache) {
- /* Only init min and max when you have a non-custom bvhtree pending. */
- init_min_max = false;
- if (sod->treedata_editmesh.cached) {
- sod->treedata_editmesh.tree = NULL;
- init_min_max = true;
- }
- for (int i = 0; i < ARRAY_SIZE(sod->bvhtree); i++) {
- if (sod->cached[i]) {
- sod->bvhtree[i] = NULL;
- init_min_max = true;
- }
- }
- }
-
- if (init_min_max) {
- bm_mesh_minmax(em->bm, sod->min, sod->max);
- }
-
- sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob);
+ sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
+ bm_mesh_minmax(em->bm, sod->min, sod->max);
}
return sod;
@@ -367,9 +446,9 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
* \{ */
typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool is_object_active,
void *data);
@@ -386,10 +465,17 @@ static void iter_snap_objects(SnapObjectContext *sctx,
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
const View3D *v3d = sctx->v3d_data.v3d;
const eSnapSelect snap_select = params->snap_select;
- const bool use_object_edit_cage = params->use_object_edit_cage;
+ const eSnapEditType edit_mode_type = params->edit_mode_type;
const bool use_backface_culling = params->use_backface_culling;
Base *base_act = view_layer->basact;
+ if (snap_select == SNAP_ONLY_ACTIVE) {
+ Object *obj_eval = DEG_get_evaluated_object(depsgraph, base_act->object);
+ sob_callback(
+ sctx, obj_eval, obj_eval->obmat, edit_mode_type, use_backface_culling, true, data);
+ return;
+ }
+
for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
if (!BASE_VISIBLE(v3d, base)) {
continue;
@@ -415,14 +501,16 @@ static void iter_snap_objects(SnapObjectContext *sctx,
}
Object *obj_eval = DEG_get_evaluated_object(depsgraph, base->object);
- if (obj_eval->transflag & OB_DUPLI) {
- DupliObject *dupli_ob;
+ if (obj_eval->transflag & OB_DUPLI ||
+ (obj_eval->runtime.geometry_set_eval != NULL &&
+ BKE_geometry_set_has_instances(obj_eval->runtime.geometry_set_eval))) {
ListBase *lb = object_duplilist(depsgraph, sctx->scene, obj_eval);
- for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
+ for (DupliObject *dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
+ BLI_assert(DEG_is_evaluated_object(dupli_ob->ob));
sob_callback(sctx,
dupli_ob->ob,
dupli_ob->mat,
- use_object_edit_cage,
+ edit_mode_type,
use_backface_culling,
is_object_active,
data);
@@ -433,7 +521,7 @@ static void iter_snap_objects(SnapObjectContext *sctx,
sob_callback(sctx,
obj_eval,
obj_eval->obmat,
- use_object_edit_cage,
+ edit_mode_type,
use_backface_culling,
is_object_active,
data);
@@ -461,7 +549,7 @@ struct RayCastAll_Data {
float len_diff;
float local_scale;
- Object *ob;
+ Object *ob_eval;
uint ob_uuid;
/* output data */
@@ -473,7 +561,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth,
const float co[3],
const float no[3],
int index,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
uint ob_uuid)
{
@@ -484,7 +572,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth,
copy_v3_v3(hit->no, no);
hit->index = index;
- hit->ob = ob;
+ hit->ob_eval = ob_eval;
copy_m4_m4(hit->obmat, (float(*)[4])obmat);
hit->ob_uuid = ob_uuid;
@@ -526,7 +614,7 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH
normalize_v3(normal);
struct SnapObjectHitDepth *hit_item = hit_depth_create(
- depth, location, normal, hit->index, data->ob, data->obmat, data->ob_uuid);
+ depth, location, normal, hit->index, data->ob_eval, data->obmat, data->ob_uuid);
BLI_addtail(data->hit_list, hit_item);
}
}
@@ -598,8 +686,8 @@ static void editmesh_looptri_raycast_backface_culling_cb(void *userdata,
static bool raycastMesh(SnapObjectContext *sctx,
const float ray_start[3],
const float ray_dir[3],
- Object *ob,
- Mesh *me,
+ Object *ob_eval,
+ Mesh *me_eval,
const float obmat[4][4],
const uint ob_index,
bool use_hide,
@@ -614,7 +702,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
{
bool retval = false;
- if (me->totpoly == 0) {
+ if (me_eval->totpoly == 0) {
return retval;
}
@@ -638,7 +726,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
}
/* Test BoundBox */
- BoundBox *bb = BKE_mesh_boundbox_get(ob);
+ BoundBox *bb = BKE_mesh_boundbox_get(ob_eval);
if (bb) {
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
@@ -658,53 +746,18 @@ static bool raycastMesh(SnapObjectContext *sctx,
len_diff = 0.0f;
}
- SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob);
+ SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
BVHTreeFromMesh *treedata = &sod->treedata_mesh;
- /* The tree is owned by the Mesh and may have been freed since we last used. */
- if (treedata->tree) {
- BLI_assert(treedata->cached);
- if (!bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) {
- free_bvhtree_from_mesh(treedata);
- }
- else {
- /* Update Pointers. */
- if (treedata->vert && treedata->vert_allocated == false) {
- treedata->vert = me->mvert;
- }
- if (treedata->loop && treedata->loop_allocated == false) {
- treedata->loop = me->mloop;
- }
- if (treedata->looptri && treedata->looptri_allocated == false) {
- treedata->looptri = BKE_mesh_runtime_looptri_ensure(me);
- }
- /* required for snapping with occlusion. */
- treedata->edge = me->medge;
- sod->poly = me->mpoly;
- }
- }
-
if (treedata->tree == NULL) {
- if (use_hide) {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI_NO_HIDDEN, 4);
- }
- else {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
- }
-
- /* required for snapping with occlusion. */
- treedata->edge = me->medge;
- sod->poly = me->mpoly;
-
- if (treedata->tree == NULL) {
- return retval;
- }
+ return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
transpose_m3_m4(timat, imat);
+ BLI_assert(treedata->raycast_callback != NULL);
if (r_hit_list) {
struct RayCastAll_Data data;
@@ -714,7 +767,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
- data.ob = ob;
+ data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
@@ -773,7 +826,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
static bool raycastEditMesh(SnapObjectContext *sctx,
const float ray_start[3],
const float ray_dir[3],
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
const uint ob_index,
@@ -810,7 +863,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
local_depth *= local_scale;
}
- SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em);
+ SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
@@ -836,7 +889,8 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
if (treedata->tree == NULL) {
/* Operators only update the editmesh looptris of the original mesh. */
- BLI_assert(sod->treedata_editmesh.em == BKE_editmesh_from_object(DEG_get_original_object(ob)));
+ BLI_assert(sod->treedata_editmesh.em ==
+ BKE_editmesh_from_object(DEG_get_original_object(ob_eval)));
em = sod->treedata_editmesh.em;
if (sctx->callbacks.edit_mesh.test_face_fn) {
@@ -883,7 +937,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
- data.ob = ob;
+ data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
@@ -964,9 +1018,9 @@ struct RaycastObjUserData {
* \note Duplicate args here are documented at #snapObjectsRay
*/
static void raycast_obj_fn(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool is_object_active,
void *data)
@@ -979,53 +1033,46 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
bool retval = false;
if (use_occlusion_test) {
- if (use_obedit && sctx->use_v3d && XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) {
+ if ((edit_mode_type == SNAP_GEOM_EDIT) && sctx->use_v3d &&
+ XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) {
/* Use of occlude geometry in editing mode disabled. */
return;
}
- if (ELEM(ob->dt, OB_BOUNDBOX, OB_WIRE)) {
+ if (ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) {
/* Do not hit objects that are in wire or bounding box
* display mode. */
return;
}
}
- switch (ob->type) {
+ switch (ob_eval->type) {
case OB_MESH: {
- Mesh *me = ob->data;
bool use_hide = false;
- if (BKE_object_is_in_editmode(ob)) {
- if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) {
- /* Operators only update the editmesh looptris of the original mesh. */
- BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob));
- retval = raycastEditMesh(sctx,
- dt->ray_start,
- dt->ray_dir,
- ob,
- em_orig,
- obmat,
- ob_index,
- use_backface_culling,
- ray_depth,
- dt->r_loc,
- dt->r_no,
- dt->r_index,
- dt->r_hit_list);
- break;
- }
-
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- me = em->mesh_eval_final;
- use_hide = true;
- }
+ Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
+ if (me_eval == NULL) {
+ /* Operators only update the editmesh looptris of the original mesh. */
+ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval));
+ retval = raycastEditMesh(sctx,
+ dt->ray_start,
+ dt->ray_dir,
+ ob_eval,
+ em_orig,
+ obmat,
+ ob_index,
+ use_backface_culling,
+ ray_depth,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index,
+ dt->r_hit_list);
+ break;
}
retval = raycastMesh(sctx,
dt->ray_start,
dt->ray_dir,
- ob,
- me,
+ ob_eval,
+ me_eval,
obmat,
ob_index,
use_hide,
@@ -1041,12 +1088,12 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
case OB_SURF:
case OB_FONT: {
if (!is_object_active) {
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
if (mesh_eval) {
retval = raycastMesh(sctx,
dt->ray_start,
dt->ray_dir,
- ob,
+ ob_eval,
mesh_eval,
obmat,
ob_index,
@@ -1065,7 +1112,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
if (retval) {
if (dt->r_ob) {
- *dt->r_ob = ob;
+ *dt->r_ob = ob_eval;
}
if (dt->r_obmat) {
copy_m4_m4(dt->r_obmat, obmat);
@@ -1320,6 +1367,33 @@ typedef struct Nearest2dUserData {
bool use_backface_culling;
} Nearest2dUserData;
+static void nearest2d_data_init(SnapObjectData *sod,
+ bool is_persp,
+ bool use_backface_culling,
+ Nearest2dUserData *r_nearest2d)
+{
+ if (sod->type == SNAP_MESH) {
+ r_nearest2d->userdata = &sod->treedata_mesh;
+ r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
+ r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
+ r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
+ r_nearest2d->get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get;
+ r_nearest2d->get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get;
+ }
+ else {
+ BLI_assert(sod->type == SNAP_EDIT_MESH);
+ r_nearest2d->userdata = sod->treedata_editmesh.em;
+ r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
+ r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
+ r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
+ r_nearest2d->get_tri_verts_index = NULL;
+ r_nearest2d->get_tri_edges_index = NULL;
+ }
+
+ r_nearest2d->is_persp = is_persp;
+ r_nearest2d->use_backface_culling = use_backface_culling;
+}
+
static void cb_snap_vert(void *userdata,
int index,
const struct DistProjectedAABBPrecalc *precalc,
@@ -1467,7 +1541,7 @@ static void cb_snap_tri_verts(void *userdata,
static short snap_mesh_polygon(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
bool use_backface_culling,
/* read/write args */
@@ -1492,28 +1566,21 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
}
- Nearest2dUserData nearest2d = {
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
-
BVHTreeNearest nearest = {
.index = -1,
.dist_sq = square_f(*dist_px),
};
- SnapObjectData *sod = snap_object_data_lookup(sctx, ob);
-
+ SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval);
BLI_assert(sod != NULL);
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
+
if (sod->type == SNAP_MESH) {
BVHTreeFromMesh *treedata = &sod->treedata_mesh;
- nearest2d.userdata = treedata;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
-
const MPoly *mp = &sod->poly[*r_index];
const MLoop *ml = &treedata->loop[mp->loopstart];
if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) {
@@ -1544,11 +1611,6 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
BLI_assert(sod->type == SNAP_EDIT_MESH);
BMEditMesh *em = sod->treedata_editmesh.em;
- nearest2d.userdata = em;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
-
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
BMFace *f = BM_face_at_index(em->bm, *r_index);
BMLoop *l_iter, *l_first;
@@ -1605,7 +1667,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
float original_dist_px,
const float prev_co[3],
@@ -1619,32 +1681,16 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
{
short elem = SCE_SNAP_MODE_EDGE;
- if (ob->type != OB_MESH) {
+ if (ob_eval->type != OB_MESH) {
return elem;
}
- SnapObjectData *sod = snap_object_data_lookup(sctx, ob);
-
+ SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval);
BLI_assert(sod != NULL);
Nearest2dUserData nearest2d;
- {
- nearest2d.is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
- nearest2d.use_backface_culling = use_backface_culling;
- if (sod->type == SNAP_MESH) {
- nearest2d.userdata = &sod->treedata_mesh;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
- }
- else {
- BLI_assert(sod->type == SNAP_EDIT_MESH);
- nearest2d.userdata = sod->treedata_editmesh.em;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
- }
- }
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
int vindex[2];
nearest2d.get_edge_verts_index(*r_index, vindex, nearest2d.userdata);
@@ -1769,9 +1815,8 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
}
static short snapArmature(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
- bool use_obedit,
/* read/write args */
float *dist_px,
/* return args */
@@ -1792,11 +1837,11 @@ static short snapArmature(SnapData *snapdata,
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
- use_obedit = use_obedit && BKE_object_is_in_editmode(ob);
+ bool use_obedit = ((bArmature *)ob_eval->data)->edbo != NULL;
if (use_obedit == false) {
/* Test BoundBox */
- BoundBox *bb = BKE_armature_boundbox_get(ob);
+ BoundBox *bb = BKE_armature_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return retval;
@@ -1811,7 +1856,7 @@ static short snapArmature(SnapData *snapdata,
bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
- bArmature *arm = ob->data;
+ bArmature *arm = ob_eval->data;
if (arm->edbo) {
LISTBASE_FOREACH (EditBone *, eBone, arm->edbo) {
if (eBone->layer & arm->layer) {
@@ -1855,8 +1900,8 @@ static short snapArmature(SnapData *snapdata,
}
}
}
- else if (ob->pose && ob->pose->chanbase.first) {
- LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ else if (ob_eval->pose && ob_eval->pose->chanbase.first) {
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_eval->pose->chanbase) {
Bone *bone = pchan->bone;
/* skip hidden bones */
if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) {
@@ -1914,7 +1959,7 @@ static short snapArmature(SnapData *snapdata,
}
static short snapCurve(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
bool use_obedit,
/* read/write args */
@@ -1931,7 +1976,7 @@ static short snapCurve(SnapData *snapdata,
return 0;
}
- Curve *cu = ob->data;
+ Curve *cu = ob_eval->data;
float dist_px_sq = square_f(*dist_px);
float lpmat[4][4];
@@ -1941,11 +1986,11 @@ static short snapCurve(SnapData *snapdata,
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
- use_obedit = use_obedit && BKE_object_is_in_editmode(ob);
+ use_obedit = use_obedit && BKE_object_is_in_editmode(ob_eval);
if (use_obedit == false) {
/* Test BoundBox */
- BoundBox *bb = BKE_curve_boundbox_get(ob);
+ BoundBox *bb = BKE_curve_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return 0;
@@ -2065,7 +2110,7 @@ static short snapCurve(SnapData *snapdata,
/* may extend later (for now just snaps to empty center) */
static short snap_object_center(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
/* read/write args */
float *dist_px,
@@ -2076,7 +2121,7 @@ static short snap_object_center(SnapData *snapdata,
{
short retval = 0;
- if (ob->transflag & OB_DUPLI) {
+ if (ob_eval->transflag & OB_DUPLI) {
return retval;
}
@@ -2219,9 +2264,10 @@ static short snapCamera(const SnapObjectContext *sctx,
static short snapMesh(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
- Mesh *me,
+ Object *ob_eval,
+ Mesh *me_eval,
const float obmat[4][4],
+ bool use_hide,
bool use_backface_culling,
/* read/write args */
float *dist_px,
@@ -2231,16 +2277,11 @@ static short snapMesh(SnapObjectContext *sctx,
int *r_index)
{
BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE);
-
- if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
- if (me->totvert == 0) {
- return 0;
- }
+ if (me_eval->totvert == 0) {
+ return 0;
}
- else {
- if (me->totedge == 0) {
- return 0;
- }
+ if (me_eval->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
+ return 0;
}
float lpmat[4][4];
@@ -2249,65 +2290,46 @@ static short snapMesh(SnapObjectContext *sctx,
float dist_px_sq = square_f(*dist_px);
/* Test BoundBox */
- BoundBox *bb = BKE_mesh_boundbox_get(ob);
+ BoundBox *bb = BKE_mesh_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return 0;
}
- SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob);
+ SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
- BVHTreeFromMesh *treedata, dummy_treedata;
+ BVHTreeFromMesh *treedata, treedata_tmp;
treedata = &sod->treedata_mesh;
- /* The tree is owned by the Mesh and may have been freed since we last used! */
- if (treedata->cached && treedata->tree &&
- !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) {
- free_bvhtree_from_mesh(treedata);
- }
- if (sod->cached[0] && sod->bvhtree[0] &&
- !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[0])) {
- sod->bvhtree[0] = NULL;
- }
- if (sod->cached[1] && sod->bvhtree[1] &&
- !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[1])) {
- sod->bvhtree[1] = NULL;
- }
-
- if (sod->has_looptris && treedata->tree == NULL) {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
- sod->has_looptris = (treedata->tree != NULL);
- if (sod->has_looptris) {
- /* Make sure that the array of edges is referenced in the callbacks. */
- treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */
- }
- }
if (sod->has_loose_edge && sod->bvhtree[0] == NULL) {
- sod->bvhtree[0] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEEDGES, 2);
- sod->has_loose_edge = sod->bvhtree[0] != NULL;
- sod->cached[0] = dummy_treedata.cached;
-
- if (sod->has_loose_edge) {
- BLI_assert(treedata->vert_allocated == false);
- treedata->vert = dummy_treedata.vert;
- treedata->vert_allocated = dummy_treedata.vert_allocated;
-
- BLI_assert(treedata->edge_allocated == false);
- treedata->edge = dummy_treedata.edge;
- treedata->edge_allocated = dummy_treedata.edge_allocated;
+ sod->bvhtree[0] = BKE_bvhtree_from_mesh_get(
+ &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEEDGES, 2);
+ if (sod->bvhtree[0] == NULL) {
+ sod->has_loose_edge = false;
}
+ sod->cached[0] = treedata_tmp.cached;
+ BLI_assert(!ELEM(true,
+ treedata_tmp.vert_allocated,
+ treedata_tmp.edge_allocated,
+ treedata_tmp.face_allocated,
+ treedata_tmp.loop_allocated,
+ treedata_tmp.looptri_allocated));
}
+
if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) {
if (sod->has_loose_vert && sod->bvhtree[1] == NULL) {
- sod->bvhtree[1] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEVERTS, 2);
- sod->has_loose_vert = sod->bvhtree[1] != NULL;
- sod->cached[1] = dummy_treedata.cached;
-
- if (sod->has_loose_vert) {
- BLI_assert(treedata->vert_allocated == false);
- treedata->vert = dummy_treedata.vert;
- treedata->vert_allocated = dummy_treedata.vert_allocated;
+ sod->bvhtree[1] = BKE_bvhtree_from_mesh_get(
+ &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEVERTS, 2);
+ if (sod->bvhtree[1] == NULL) {
+ sod->has_loose_vert = false;
}
+ sod->cached[1] = treedata_tmp.cached;
+ BLI_assert(!ELEM(true,
+ treedata_tmp.vert_allocated,
+ treedata_tmp.edge_allocated,
+ treedata_tmp.face_allocated,
+ treedata_tmp.loop_allocated,
+ treedata_tmp.looptri_allocated));
}
}
else {
@@ -2315,33 +2337,9 @@ static short snapMesh(SnapObjectContext *sctx,
sod->has_loose_vert = false;
}
- /* Update pointers. */
- if (treedata->vert_allocated == false) {
- treedata->vert = me->mvert; /* CustomData_get_layer(&me->vdata, CD_MVERT);? */
- }
- if (treedata->tree || sod->bvhtree[0]) {
- if (treedata->edge_allocated == false) {
- /* If raycast has been executed before, `treedata->edge` can be NULL. */
- treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */
- }
- if (treedata->loop && treedata->loop_allocated == false) {
- treedata->loop = me->mloop; /* CustomData_get_layer(&me->edata, CD_MLOOP);? */
- }
- if (treedata->looptri && treedata->looptri_allocated == false) {
- treedata->looptri = BKE_mesh_runtime_looptri_ensure(me);
- }
- }
-
- Nearest2dUserData nearest2d = {
- .userdata = treedata,
- .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get,
- .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get,
- .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get,
- .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get,
- .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy,
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
BVHTreeNearest nearest = {
.index = -1,
@@ -2457,7 +2455,7 @@ static short snapMesh(SnapObjectContext *sctx,
static short snapEditMesh(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
bool use_backface_culling,
@@ -2486,7 +2484,7 @@ static short snapEditMesh(SnapObjectContext *sctx,
float dist_px_sq = square_f(*dist_px);
- SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em);
+ SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
@@ -2560,14 +2558,9 @@ static short snapEditMesh(SnapObjectContext *sctx,
}
}
- Nearest2dUserData nearest2d = {
- .userdata = em,
- .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get,
- .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get,
- .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy,
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
BVHTreeNearest nearest = {
.index = -1,
@@ -2661,9 +2654,9 @@ struct SnapObjUserData {
* \note Duplicate args here are documented at #snapObjectsRay
*/
static void snap_obj_fn(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool UNUSED(is_object_active),
void *data)
@@ -2671,41 +2664,36 @@ static void snap_obj_fn(SnapObjectContext *sctx,
struct SnapObjUserData *dt = data;
short retval = 0;
- switch (ob->type) {
+ switch (ob_eval->type) {
case OB_MESH: {
- Mesh *me = ob->data;
- if (BKE_object_is_in_editmode(ob)) {
- if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) {
- /* Operators only update the editmesh looptris of the original mesh. */
- BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob));
- retval = snapEditMesh(sctx,
- dt->snapdata,
- ob,
- em_orig,
- obmat,
- use_backface_culling,
- dt->dist_px,
- dt->r_loc,
- dt->r_no,
- dt->r_index);
- break;
- }
-
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- me = em->mesh_eval_final;
- }
+ bool use_hide;
+ Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
+ if (me_eval == NULL) {
+ /* Operators only update the editmesh looptris of the original mesh. */
+ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval));
+ retval = snapEditMesh(sctx,
+ dt->snapdata,
+ ob_eval,
+ em_orig,
+ obmat,
+ use_backface_culling,
+ dt->dist_px,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ break;
}
- else if (ob->dt == OB_BOUNDBOX) {
+ if (ob_eval->dt == OB_BOUNDBOX) {
/* Do not snap to objects that are in bounding box display mode */
return;
}
retval = snapMesh(sctx,
dt->snapdata,
- ob,
- me,
+ ob_eval,
+ me_eval,
obmat,
+ use_hide,
use_backface_culling,
dt->dist_px,
dt->r_loc,
@@ -2715,21 +2703,28 @@ static void snap_obj_fn(SnapObjectContext *sctx,
}
case OB_ARMATURE:
retval = snapArmature(
- dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
case OB_CURVE:
- retval = snapCurve(
- dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ retval = snapCurve(dt->snapdata,
+ ob_eval,
+ obmat,
+ edit_mode_type == SNAP_GEOM_EDIT,
+ dt->dist_px,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */
case OB_SURF:
case OB_FONT: {
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
if (mesh_eval) {
retval |= snapMesh(sctx,
dt->snapdata,
- ob,
+ ob_eval,
mesh_eval,
obmat,
+ false,
use_backface_culling,
dt->dist_px,
dt->r_loc,
@@ -2742,17 +2737,17 @@ static void snap_obj_fn(SnapObjectContext *sctx,
case OB_GPENCIL:
case OB_LAMP:
retval = snap_object_center(
- dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
case OB_CAMERA:
retval = snapCamera(
- sctx, dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ sctx, dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
}
if (retval) {
if (dt->r_ob) {
- *dt->r_ob = ob;
+ *dt->r_ob = ob_eval;
}
if (dt->r_obmat) {
copy_m4_m4(dt->r_obmat, obmat);
@@ -3025,7 +3020,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
short retval = 0;
bool has_hit = false;
- Object *ob = NULL;
+ Object *ob_eval = NULL;
float loc[3];
/* Not all snapping callbacks set the normal,
* initialize this since any hit copies both the `loc` and `no`. */
@@ -3062,7 +3057,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
loc,
no,
&index,
- &ob,
+ &ob_eval,
obmat,
NULL);
@@ -3074,7 +3069,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
copy_v3_v3(r_no, no);
}
if (r_ob) {
- *r_ob = ob;
+ *r_ob = ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
@@ -3110,9 +3105,10 @@ static short transform_snap_context_project_view3d_mixed_impl(
snapdata.has_occlusion_plane = false;
/* By convention we only snap to the original elements of a curve. */
- if (has_hit && ob->type != OB_CURVE) {
+ if (has_hit && ob_eval->type != OB_CURVE) {
/* Compute the new clip_pane but do not add it yet. */
float new_clipplane[4];
+ BLI_ASSERT_UNIT_V3(no);
plane_from_point_normal_v3(new_clipplane, loc, no);
if (dot_v3v3(snapdata.clip_plane[0], new_clipplane) > 0.0f) {
/* The plane is facing the wrong direction. */
@@ -3123,8 +3119,15 @@ static short transform_snap_context_project_view3d_mixed_impl(
new_clipplane[3] += 0.01f;
/* Try to snap only to the polygon. */
- elem_test = snap_mesh_polygon(
- sctx, &snapdata, ob, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index);
+ elem_test = snap_mesh_polygon(sctx,
+ &snapdata,
+ ob_eval,
+ obmat,
+ params->use_backface_culling,
+ &dist_px_tmp,
+ loc,
+ no,
+ &index);
if (elem_test) {
elem = elem_test;
}
@@ -3139,7 +3142,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
}
elem_test = snapObjectsRay(
- sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob, obmat);
+ sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat);
if (elem_test) {
elem = elem_test;
}
@@ -3150,7 +3153,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
snapdata.snap_to_flag = snap_to_flag;
elem = snap_mesh_edge_verts_mixed(sctx,
&snapdata,
- ob,
+ ob_eval,
obmat,
*dist_px,
prev_co,
@@ -3169,7 +3172,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
copy_v3_v3(r_no, no);
}
if (r_ob) {
- *r_ob = ob;
+ *r_ob = ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index b69f62a2875..7811509ab40 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -273,9 +273,11 @@ static void ed_undo_step_post(bContext *C,
}
}
-/** Undo or redo one step from current active one.
- * May undo or redo several steps at once only if the target step is a 'skipped' one.
- * The target step will be the one immediately before or after the active one. */
+/**
+ * Undo or redo one step from current active one.
+ * May undo or redo several steps at once only if the target step is a 'skipped' one.
+ * The target step will be the one immediately before or after the active one.
+ */
static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportList *reports)
{
BLI_assert(ELEM(step, STEP_UNDO, STEP_REDO));
@@ -308,9 +310,11 @@ static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportLis
return OPERATOR_FINISHED;
}
-/** Undo the step matching given name.
- * May undo several steps at once.
- * The target step will be the one immediately before given named one. */
+/**
+ * Undo the step matching given name.
+ * May undo several steps at once.
+ * The target step will be the one immediately before given named one.
+ */
static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList *reports)
{
BLI_assert(undo_name != NULL);
@@ -354,9 +358,11 @@ static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList *
return OPERATOR_FINISHED;
}
-/** Load the step matching given index in the stack.
- * May undo or redo several steps at once.
- * The target step will be the one indicated by the given index. */
+/**
+ * Load the step matching given index in the stack.
+ * May undo or redo several steps at once.
+ * The target step will be the one indicated by the given index.
+ */
static int ed_undo_step_by_index(bContext *C, const int undo_index, ReportList *reports)
{
BLI_assert(undo_index >= 0);
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 7d7d10004a3..54ec6b22e70 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -18,10 +18,10 @@
set(INC
../include
../space_sequencer
+ ../../blenfont
../../blenkernel
../../blenlib
../../blentranslation
- ../../blenfont
../../bmesh
../../depsgraph
../../gpu
@@ -86,6 +86,7 @@ set(SRC
../include/ED_sequencer.h
../include/ED_sound.h
../include/ED_space_api.h
+ ../include/ED_spreadsheet.h
../include/ED_text.h
../include/ED_time_scrub_ui.h
../include/ED_transform.h
diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c
index d7b22b4f601..3b543cdf6ce 100644
--- a/source/blender/editors/util/ed_draw.c
+++ b/source/blender/editors/util/ed_draw.c
@@ -189,7 +189,7 @@ static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const
ofs_y += vertical_offset;
}
} /* Strip */
- else if (i == 1 || i == 2) {
+ else if (ELEM(i, 1, 2)) {
int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c
index d0234dee856..c1d0dcdb095 100644
--- a/source/blender/editors/util/ed_transverts.c
+++ b/source/blender/editors/util/ed_transverts.c
@@ -54,7 +54,7 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit)
const int mode = tvs->mode;
BLI_assert(ED_transverts_check_obedit(obedit) == true);
- DEG_id_tag_update(obedit->data, 0);
+ DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
if (obedit->type == OB_MESH) {
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -111,7 +111,9 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit)
}
}
- BKE_nurb_test_2d(nu);
+ if (CU_IS_2D(cu)) {
+ BKE_nurb_project_2d(nu);
+ }
BKE_nurb_handles_test(nu, true, false); /* test for bezier too */
nu = nu->next;
}
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index da94eef4917..b80782b51be 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -27,9 +27,6 @@
#include "MEM_guardedalloc.h"
-#include "DNA_armature_types.h"
-#include "DNA_mesh_types.h"
-
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
@@ -206,24 +203,9 @@ void ED_editors_exit(Main *bmain, bool do_undo_system)
*
* To reproduce the problem where stale data is used, see: T84920. */
for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) {
- if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
- if (me->edit_mesh) {
- EDBM_mesh_free(me->edit_mesh);
- MEM_freeN(me->edit_mesh);
- me->edit_mesh = NULL;
- if (do_undo_system == false) {
- DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- }
- }
- }
- else if (ob->type == OB_ARMATURE) {
- bArmature *arm = ob->data;
- if (arm->edbo) {
- ED_armature_edit_free(ob->data);
- if (do_undo_system == false) {
- DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- }
+ if (ED_object_editmode_free_ex(bmain, ob)) {
+ if (do_undo_system == false) {
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
}
}
diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc
index 06f1b999d58..462f7768f81 100644
--- a/source/blender/editors/util/ed_util_ops.cc
+++ b/source/blender/editors/util/ed_util_ops.cc
@@ -149,7 +149,7 @@ static void ED_OT_lib_id_generate_preview(wmOperatorType *ot)
ot->exec = lib_id_generate_preview_exec;
/* flags */
- ot->flag = OPTYPE_INTERNAL;
+ ot->flag = OPTYPE_INTERNAL | OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index 91ec8546225..15d672dea56 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -29,6 +29,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_unit.h"
@@ -283,10 +284,15 @@ bool user_string_to_number(bContext *C,
const char *str,
const UnitSettings *unit,
int type,
- const char *error_prefix,
- double *r_value)
+ double *r_value,
+ const bool use_single_line_error,
+ char **r_error)
{
#ifdef WITH_PYTHON
+ struct BPy_RunErrInfo err_info = {
+ .use_single_line_error = use_single_line_error,
+ .r_string = r_error,
+ };
double unit_scale = BKE_scene_unit_scale(unit, type, 1.0);
if (BKE_unit_string_contains_unit(str, type)) {
char str_unit_convert[256];
@@ -294,10 +300,10 @@ bool user_string_to_number(bContext *C,
BKE_unit_replace_string(
str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type);
- return BPY_run_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value);
+ return BPY_run_string_as_number(C, NULL, str_unit_convert, &err_info, r_value);
}
- int success = BPY_run_string_as_number(C, NULL, str, error_prefix, r_value);
+ int success = BPY_run_string_as_number(C, NULL, str, &err_info, r_value);
*r_value = BKE_unit_apply_preferred_unit(unit, type, *r_value);
*r_value /= unit_scale;
return success;
@@ -577,10 +583,19 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
if (n->str[0]) {
const float val_prev = n->val[idx];
Scene *sce = CTX_data_scene(C);
+ char *error = NULL;
double val;
int success = user_string_to_number(
- C, n->str, &sce->unit, n->unit_type[idx], IFACE_("Numeric input evaluation"), &val);
+ C, n->str, &sce->unit, n->unit_type[idx], &val, false, &error);
+
+ if (error) {
+ ReportList *reports = CTX_wm_reports(C);
+ printf("%s\n", error);
+ BKE_report(reports, RPT_ERROR, error);
+ BKE_report(reports, RPT_ERROR, IFACE_("Numeric input evaluation"));
+ MEM_freeN(error);
+ }
if (success) {
n->val[idx] = (float)val;
diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c
index 14a6d751bb1..4e8cf1e92e6 100644
--- a/source/blender/editors/util/select_utils.c
+++ b/source/blender/editors/util/select_utils.c
@@ -88,11 +88,11 @@ int ED_select_similar_compare_float(const float delta, const float thresh, const
{
switch (compare) {
case SIM_CMP_EQ:
- return (fabsf(delta) < thresh + FLT_EPSILON);
+ return (fabsf(delta) <= thresh);
case SIM_CMP_GT:
- return ((delta + thresh) > -FLT_EPSILON);
+ return ((delta + thresh) >= 0.0);
case SIM_CMP_LT:
- return ((delta - thresh) < FLT_EPSILON);
+ return ((delta - thresh) <= 0.0);
default:
BLI_assert_unreachable();
return 0;
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index e11341429a6..708f04bf044 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -1471,7 +1471,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
if (EDBM_mesh_hide(em, swap)) {
EDBM_update_generic(ob->data, true, false);
}
- return OPERATOR_FINISHED;
+ continue;
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -1609,7 +1609,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
if (EDBM_mesh_reveal(em, select)) {
EDBM_update_generic(ob->data, true, false);
}
- return OPERATOR_FINISHED;
+ continue;
}
if (use_face_center) {
if (em->selectmode == SCE_SELECT_FACE) {
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c
index e94aaa49839..7d82884760c 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.c
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.c
@@ -4546,7 +4546,10 @@ void param_edge_set_seam(ParamHandle *handle, ParamKey *vkeys)
}
}
-void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool topology_from_uvs)
+void param_construct_end(ParamHandle *handle,
+ ParamBool fill,
+ ParamBool topology_from_uvs,
+ int *count_fail)
{
PHandle *phandle = (PHandle *)handle;
PChart *chart = phandle->construction_chart;
@@ -4574,6 +4577,9 @@ void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool topology
if (!topology_from_uvs && nboundaries == 0) {
p_chart_delete(chart);
+ if (count_fail != NULL) {
+ *count_fail += 1;
+ }
continue;
}
@@ -4611,12 +4617,11 @@ void param_lscm_begin(ParamHandle *handle, ParamBool live, ParamBool abf)
}
}
-void param_lscm_solve(ParamHandle *handle)
+void param_lscm_solve(ParamHandle *handle, int *count_changed, int *count_failed)
{
PHandle *phandle = (PHandle *)handle;
PChart *chart;
int i;
- PBool result;
param_assert(phandle->state == PHANDLE_STATE_LSCM);
@@ -4624,7 +4629,7 @@ void param_lscm_solve(ParamHandle *handle)
chart = phandle->charts[i];
if (chart->u.lscm.context) {
- result = p_chart_lscm_solve(phandle, chart);
+ const PBool result = p_chart_lscm_solve(phandle, chart);
if (result && !(chart->flag & PCHART_HAS_PINS)) {
p_chart_rotate_minimum_area(chart);
@@ -4637,6 +4642,17 @@ void param_lscm_solve(ParamHandle *handle)
if (!result || !(chart->flag & PCHART_HAS_PINS)) {
p_chart_lscm_end(chart);
}
+
+ if (result) {
+ if (count_changed != NULL) {
+ *count_changed += 1;
+ }
+ }
+ else {
+ if (count_failed != NULL) {
+ *count_failed += 1;
+ }
+ }
}
}
}
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.h b/source/blender/editors/uvedit/uvedit_parametrizer.h
index 2427e589833..e69ce360e61 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.h
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.h
@@ -59,7 +59,10 @@ void param_face_add(ParamHandle *handle,
void param_edge_set_seam(ParamHandle *handle, ParamKey *vkeys);
-void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool topology_from_uvs);
+void param_construct_end(ParamHandle *handle,
+ ParamBool fill,
+ ParamBool topology_from_uvs,
+ int *count_fail);
void param_delete(ParamHandle *handle);
/* Least Squares Conformal Maps:
@@ -74,7 +77,7 @@ void param_delete(ParamHandle *handle);
*/
void param_lscm_begin(ParamHandle *handle, ParamBool live, ParamBool abf);
-void param_lscm_solve(ParamHandle *handle);
+void param_lscm_solve(ParamHandle *handle, int *count_changed, int *count_failed);
void param_lscm_end(ParamHandle *handle);
/* Stretch */
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index c10e132a4e2..39950f9d0c7 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -1381,7 +1381,7 @@ static void uv_select_linked_multi(Scene *scene,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
bool add_to_stack = true;
- if (uv_sync_select && !select_faces) {
+ if (uv_sync_select) {
/* Special case, vertex/edge & sync select being enabled.
*
* Without this, a second linked select will 'grow' each time as each new
@@ -1392,6 +1392,7 @@ static void uv_select_linked_multi(Scene *scene,
* - The only other fully selected face is connected or,
* - There are no connected fully selected faces UV-connected to this loop.
*/
+ BLI_assert(!select_faces);
if (uvedit_face_select_test(scene, l->f, cd_loop_uv_offset)) {
/* pass */
}
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index fc5f41e8ed5..87ae112a237 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -165,6 +165,11 @@ typedef struct UnwrapOptions {
bool correct_aspect;
} UnwrapOptions;
+typedef struct UnwrapResultInfo {
+ int count_changed;
+ int count_failed;
+} UnwrapResultInfo;
+
static bool uvedit_have_selection(const Scene *scene, BMEditMesh *em, const UnwrapOptions *options)
{
BMFace *efa;
@@ -281,7 +286,8 @@ static void construct_param_handle_face_add(ParamHandle *handle,
static ParamHandle *construct_param_handle(const Scene *scene,
Object *ob,
BMesh *bm,
- const UnwrapOptions *options)
+ const UnwrapOptions *options,
+ UnwrapResultInfo *result_info)
{
ParamHandle *handle;
BMFace *efa;
@@ -344,7 +350,10 @@ static ParamHandle *construct_param_handle(const Scene *scene,
}
}
- param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
+ param_construct_end(handle,
+ options->fill_holes,
+ options->topology_from_uvs,
+ result_info ? &result_info->count_failed : NULL);
return handle;
}
@@ -355,7 +364,8 @@ static ParamHandle *construct_param_handle(const Scene *scene,
static ParamHandle *construct_param_handle_multi(const Scene *scene,
Object **objects,
const uint objects_len,
- const UnwrapOptions *options)
+ const UnwrapOptions *options,
+ int *count_fail)
{
ParamHandle *handle;
BMFace *efa;
@@ -431,7 +441,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
offset += bm->totface;
}
- param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
+ param_construct_end(handle, options->fill_holes, options->topology_from_uvs, count_fail);
return handle;
}
@@ -475,7 +485,8 @@ static void texface_from_original_index(const Scene *scene,
static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
Object *ob,
BMEditMesh *em,
- const UnwrapOptions *options)
+ const UnwrapOptions *options,
+ UnwrapResultInfo *result_info)
{
ParamHandle *handle;
/* index pointers */
@@ -651,7 +662,10 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
}
}
- param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
+ param_construct_end(handle,
+ options->fill_holes,
+ options->topology_from_uvs,
+ result_info ? &result_info->count_failed : NULL);
/* cleanup */
MEM_freeN(faceMap);
@@ -707,7 +721,7 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op)
ms->blend = RNA_float_get(op->ptr, "blend");
ms->iterations = RNA_int_get(op->ptr, "iterations");
ms->i = 0;
- ms->handle = construct_param_handle_multi(scene, objects, objects_len, &options);
+ ms->handle = construct_param_handle_multi(scene, objects, objects_len, &options, NULL);
ms->lasttime = PIL_check_seconds_timer();
param_stretch_begin(ms->handle);
@@ -959,7 +973,7 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm)
bool ignore_pinned = false;
ParamHandle *handle;
- handle = construct_param_handle(scene, ob, bm, &options);
+ handle = construct_param_handle(scene, ob, bm, &options, NULL);
param_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
param_flush(handle);
param_delete(handle);
@@ -979,7 +993,7 @@ static void uvedit_pack_islands_multi(const Scene *scene,
bool ignore_pinned)
{
ParamHandle *handle;
- handle = construct_param_handle_multi(scene, objects, objects_len, options);
+ handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL);
param_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
param_flush(handle);
param_delete(handle);
@@ -1087,7 +1101,7 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
- ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options);
+ ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options, NULL);
param_average(handle, false);
param_flush(handle);
param_delete(handle);
@@ -1154,10 +1168,10 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
};
if (use_subsurf) {
- handle = construct_param_handle_subsurfed(scene, obedit, em, &options);
+ handle = construct_param_handle_subsurfed(scene, obedit, em, &options, NULL);
}
else {
- handle = construct_param_handle(scene, obedit, em->bm, &options);
+ handle = construct_param_handle(scene, obedit, em->bm, &options, NULL);
}
param_lscm_begin(handle, PARAM_TRUE, abf);
@@ -1182,7 +1196,7 @@ void ED_uvedit_live_unwrap_re_solve(void)
{
if (g_live_unwrap.handles) {
for (int i = 0; i < g_live_unwrap.len; i++) {
- param_lscm_solve(g_live_unwrap.handles[i]);
+ param_lscm_solve(g_live_unwrap.handles[i], NULL, NULL);
param_flush(g_live_unwrap.handles[i]);
}
}
@@ -1631,7 +1645,10 @@ static void uv_map_clip_correct(Object *ob, wmOperator *op)
* \{ */
/* Assumes UV Map exists, doesn't run update funcs. */
-static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOptions *options)
+static void uvedit_unwrap(const Scene *scene,
+ Object *obedit,
+ const UnwrapOptions *options,
+ UnwrapResultInfo *result_info)
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
if (!CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) {
@@ -1643,14 +1660,16 @@ static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOption
ParamHandle *handle;
if (use_subsurf) {
- handle = construct_param_handle_subsurfed(scene, obedit, em, options);
+ handle = construct_param_handle_subsurfed(scene, obedit, em, options, result_info);
}
else {
- handle = construct_param_handle(scene, obedit, em->bm, options);
+ handle = construct_param_handle(scene, obedit, em->bm, options, result_info);
}
param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0);
- param_lscm_solve(handle);
+ param_lscm_solve(handle,
+ result_info ? &result_info->count_changed : NULL,
+ result_info ? &result_info->count_failed : NULL);
param_lscm_end(handle);
param_average(handle, true);
@@ -1663,11 +1682,12 @@ static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOption
static void uvedit_unwrap_multi(const Scene *scene,
Object **objects,
const int objects_len,
- const UnwrapOptions *options)
+ const UnwrapOptions *options,
+ UnwrapResultInfo *result_info)
{
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
- uvedit_unwrap(scene, obedit, options);
+ uvedit_unwrap(scene, obedit, options, result_info);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
}
@@ -1687,7 +1707,7 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len
bool rotate = true;
bool ignore_pinned = true;
- uvedit_unwrap_multi(scene, objects, objects_len, &options);
+ uvedit_unwrap_multi(scene, objects, objects_len, &options, NULL);
uvedit_pack_islands_multi(scene, objects, objects_len, &options, rotate, ignore_pinned);
}
}
@@ -1816,11 +1836,28 @@ static int unwrap_exec(bContext *C, wmOperator *op)
}
/* execute unwrap */
- uvedit_unwrap_multi(scene, objects, objects_len, &options);
+ UnwrapResultInfo result_info = {
+ .count_changed = 0,
+ .count_failed = 0,
+ };
+ uvedit_unwrap_multi(scene, objects, objects_len, &options, &result_info);
uvedit_pack_islands_multi(scene, objects, objects_len, &options, rotate, ignore_pinned);
MEM_freeN(objects);
+ if (result_info.count_failed == 0 && result_info.count_changed == 0) {
+ BKE_report(op->reports,
+ RPT_WARNING,
+ "Unwrap could not solve any island(s), edge seams may need to be added");
+ }
+ else if (result_info.count_failed) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Unwrap failed to solve %d of %d island(s), edge seams may need to be added",
+ result_info.count_failed,
+ result_info.count_changed + result_info.count_failed);
+ }
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/freestyle/intern/geometry/Bezier.cpp b/source/blender/freestyle/intern/geometry/Bezier.cpp
index 4eec3d72db9..bf4a78b12c5 100644
--- a/source/blender/freestyle/intern/geometry/Bezier.cpp
+++ b/source/blender/freestyle/intern/geometry/Bezier.cpp
@@ -26,13 +26,7 @@ using namespace std;
namespace Freestyle {
-BezierCurveSegment::BezierCurveSegment()
-{
-}
-
-BezierCurveSegment::~BezierCurveSegment()
-{
-}
+BezierCurveSegment::~BezierCurveSegment() = default;
void BezierCurveSegment::AddControlPoint(const Vec2d &iPoint)
{
diff --git a/source/blender/freestyle/intern/geometry/Bezier.h b/source/blender/freestyle/intern/geometry/Bezier.h
index 4c6b38935f9..83b033c105a 100644
--- a/source/blender/freestyle/intern/geometry/Bezier.h
+++ b/source/blender/freestyle/intern/geometry/Bezier.h
@@ -41,7 +41,6 @@ class BezierCurveSegment {
std::vector<Vec2d> _Vertices;
public:
- BezierCurveSegment();
virtual ~BezierCurveSegment();
void AddControlPoint(const Vec2d &iPoint);
diff --git a/source/blender/freestyle/intern/geometry/FitCurve.cpp b/source/blender/freestyle/intern/geometry/FitCurve.cpp
index 7c0b3bf0224..eb5a24f7373 100644
--- a/source/blender/freestyle/intern/geometry/FitCurve.cpp
+++ b/source/blender/freestyle/intern/geometry/FitCurve.cpp
@@ -462,10 +462,6 @@ static Vector2 V2SubII(Vector2 a, Vector2 b)
//------------------------- WRAPPER -----------------------------//
-FitCurveWrapper::FitCurveWrapper()
-{
-}
-
FitCurveWrapper::~FitCurveWrapper()
{
_vertices.clear();
diff --git a/source/blender/freestyle/intern/geometry/FitCurve.h b/source/blender/freestyle/intern/geometry/FitCurve.h
index c08dc52249e..dc60d67b943 100644
--- a/source/blender/freestyle/intern/geometry/FitCurve.h
+++ b/source/blender/freestyle/intern/geometry/FitCurve.h
@@ -70,7 +70,6 @@ class FitCurveWrapper {
std::vector<Vector2> _vertices;
public:
- FitCurveWrapper();
~FitCurveWrapper();
/*! Fits a set of 2D data points to a set of Bezier Curve segments
diff --git a/source/blender/freestyle/intern/geometry/GridHelpers.cpp b/source/blender/freestyle/intern/geometry/GridHelpers.cpp
index a83dc385758..ee64b83f19e 100644
--- a/source/blender/freestyle/intern/geometry/GridHelpers.cpp
+++ b/source/blender/freestyle/intern/geometry/GridHelpers.cpp
@@ -42,8 +42,6 @@ void GridHelpers::getDefaultViewProscenium(real viewProscenium[4])
viewProscenium[3] = g_freestyle.viewport[3] * (1.0f - borderZone + bufferZone);
}
-GridHelpers::Transform::~Transform()
-{
-}
+GridHelpers::Transform::~Transform() = default;
} /* namespace Freestyle */
diff --git a/source/blender/freestyle/intern/geometry/normal_cycle.cpp b/source/blender/freestyle/intern/geometry/normal_cycle.cpp
index 2310525a1e1..77a80e63b77 100644
--- a/source/blender/freestyle/intern/geometry/normal_cycle.cpp
+++ b/source/blender/freestyle/intern/geometry/normal_cycle.cpp
@@ -36,10 +36,6 @@ namespace Freestyle::OGF {
//_________________________________________________________
-NormalCycle::NormalCycle()
-{
-}
-
void NormalCycle::begin()
{
M_[0] = M_[1] = M_[2] = M_[3] = M_[4] = M_[5] = 0;
diff --git a/source/blender/freestyle/intern/geometry/normal_cycle.h b/source/blender/freestyle/intern/geometry/normal_cycle.h
index 5adef773be1..9d8ffcfd7fb 100644
--- a/source/blender/freestyle/intern/geometry/normal_cycle.h
+++ b/source/blender/freestyle/intern/geometry/normal_cycle.h
@@ -64,7 +64,6 @@ template<class T> inline void ogf_swap(T &x, T &y)
*/
class NormalCycle {
public:
- NormalCycle();
void begin();
void end();
/**
diff --git a/source/blender/freestyle/intern/scene_graph/NodeCamera.cpp b/source/blender/freestyle/intern/scene_graph/NodeCamera.cpp
index 4fc1f227172..cb30a661aaa 100644
--- a/source/blender/freestyle/intern/scene_graph/NodeCamera.cpp
+++ b/source/blender/freestyle/intern/scene_graph/NodeCamera.cpp
@@ -105,17 +105,6 @@ NodeOrthographicCamera::NodeOrthographicCamera(
projection_matrix_[11] = -(zFar + zNear) / (zFar - zNear);
}
-NodeOrthographicCamera::NodeOrthographicCamera(const NodeOrthographicCamera &iBrother)
- : NodeCamera(iBrother),
- left_(iBrother.left_),
- right_(iBrother.right_),
- bottom_(iBrother.bottom_),
- top_(iBrother.top_),
- zNear_(iBrother.zNear_),
- zFar_(iBrother.zFar_)
-{
-}
-
NodePerspectiveCamera::NodePerspectiveCamera() : NodeCamera(NodeCamera::PERSPECTIVE)
{
}
diff --git a/source/blender/freestyle/intern/scene_graph/NodeCamera.h b/source/blender/freestyle/intern/scene_graph/NodeCamera.h
index cc7b1f7f67c..9e3f9046e39 100644
--- a/source/blender/freestyle/intern/scene_graph/NodeCamera.h
+++ b/source/blender/freestyle/intern/scene_graph/NodeCamera.h
@@ -136,7 +136,7 @@ class NodeOrthographicCamera : public NodeCamera {
return zFar_;
}
- NodeOrthographicCamera(const NodeOrthographicCamera &iBrother);
+ NodeOrthographicCamera(const NodeOrthographicCamera &iBrother) = default;
private:
double left_;
diff --git a/source/blender/freestyle/intern/stroke/ChainingIterators.h b/source/blender/freestyle/intern/stroke/ChainingIterators.h
index e3d49c167b5..0a9e7114ecb 100644
--- a/source/blender/freestyle/intern/stroke/ChainingIterators.h
+++ b/source/blender/freestyle/intern/stroke/ChainingIterators.h
@@ -164,7 +164,7 @@ class ChainingIterator : public ViewEdgeInternal::ViewEdgeIterator {
* Indicates whether to force the chaining to stay within
* the set of selected ViewEdges or not.
* \param iRestrictToUnvisited:
- * Indicates whether a ViewEdge that has already been chained must be ignored ot not.
+ * Indicates whether a ViewEdge that has already been chained must be ignored or not.
* \param begin:
* The ViewEdge from which to start the chain.
* \param orientation:
diff --git a/source/blender/freestyle/intern/stroke/Curve.h b/source/blender/freestyle/intern/stroke/Curve.h
index 91470b57ca2..f0db45150a9 100644
--- a/source/blender/freestyle/intern/stroke/Curve.h
+++ b/source/blender/freestyle/intern/stroke/Curve.h
@@ -236,9 +236,7 @@ class CurvePoint : public Interface0D {
CurvePoint &operator=(const CurvePoint &iBrother);
/*! Destructor */
- virtual ~CurvePoint()
- {
- }
+ virtual ~CurvePoint() = default;
/*! Operator == */
bool operator==(const CurvePoint &b)
diff --git a/source/blender/freestyle/intern/stroke/Stroke.cpp b/source/blender/freestyle/intern/stroke/Stroke.cpp
index 79eb37ae870..0de3e03d44a 100644
--- a/source/blender/freestyle/intern/stroke/Stroke.cpp
+++ b/source/blender/freestyle/intern/stroke/Stroke.cpp
@@ -383,10 +383,6 @@ StrokeVertex::StrokeVertex(SVertex *iSVertex, const StrokeAttribute &iAttribute)
_StrokeLength = 0.0f;
}
-StrokeVertex::~StrokeVertex()
-{
-}
-
StrokeVertex &StrokeVertex::operator=(const StrokeVertex &iBrother)
{
((CurvePoint *)this)->operator=(iBrother);
diff --git a/source/blender/freestyle/intern/stroke/Stroke.h b/source/blender/freestyle/intern/stroke/Stroke.h
index 5772b80b093..4a9ed7288c5 100644
--- a/source/blender/freestyle/intern/stroke/Stroke.h
+++ b/source/blender/freestyle/intern/stroke/Stroke.h
@@ -355,9 +355,6 @@ class StrokeVertex : public CurvePoint {
/*! Builds a stroke from a view vertex and an attribute */
StrokeVertex(SVertex *iSVertex, const StrokeAttribute &iAttribute);
- /*! destructor */
- virtual ~StrokeVertex();
-
/* operators */
/*! operator = */
StrokeVertex &operator=(const StrokeVertex &iBrother);
diff --git a/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp b/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp
index a4268e43a56..797fcc1aabc 100644
--- a/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp
+++ b/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp
@@ -37,13 +37,7 @@ namespace Freestyle {
TextureManager *StrokeRenderer::_textureManager = nullptr;
-StrokeRenderer::StrokeRenderer()
-{
-}
-
-StrokeRenderer::~StrokeRenderer()
-{
-}
+StrokeRenderer::~StrokeRenderer() = default;
bool StrokeRenderer::loadTextures()
{
diff --git a/source/blender/freestyle/intern/stroke/StrokeRenderer.h b/source/blender/freestyle/intern/stroke/StrokeRenderer.h
index 2fb08b880d9..d3ed8bde8a3 100644
--- a/source/blender/freestyle/intern/stroke/StrokeRenderer.h
+++ b/source/blender/freestyle/intern/stroke/StrokeRenderer.h
@@ -121,7 +121,6 @@ class TextureManager {
* first rendering */
class StrokeRenderer {
public:
- StrokeRenderer();
virtual ~StrokeRenderer();
/*! Renders a stroke rep */
diff --git a/source/blender/freestyle/intern/system/PseudoNoise.cpp b/source/blender/freestyle/intern/system/PseudoNoise.cpp
index c05c269c404..7f568fcf460 100644
--- a/source/blender/freestyle/intern/system/PseudoNoise.cpp
+++ b/source/blender/freestyle/intern/system/PseudoNoise.cpp
@@ -41,10 +41,6 @@ namespace Freestyle {
real PseudoNoise::_values[];
-PseudoNoise::PseudoNoise()
-{
-}
-
void PseudoNoise::init(long seed)
{
RandGen::srand48(seed);
diff --git a/source/blender/freestyle/intern/system/PseudoNoise.h b/source/blender/freestyle/intern/system/PseudoNoise.h
index 38270016675..3be74bf0110 100644
--- a/source/blender/freestyle/intern/system/PseudoNoise.h
+++ b/source/blender/freestyle/intern/system/PseudoNoise.h
@@ -31,8 +31,6 @@ namespace Freestyle {
class PseudoNoise {
public:
- PseudoNoise();
-
virtual ~PseudoNoise()
{
}
diff --git a/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp b/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp
index 6c5cbc71a76..923748e6d7d 100644
--- a/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp
+++ b/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp
@@ -55,10 +55,6 @@ ArbitraryGridDensityProvider::ArbitraryGridDensityProvider(OccluderSource &sourc
initialize(proscenium);
}
-ArbitraryGridDensityProvider::~ArbitraryGridDensityProvider()
-{
-}
-
void ArbitraryGridDensityProvider::initialize(const real proscenium[4])
{
float prosceniumWidth = (proscenium[1] - proscenium[0]);
@@ -99,10 +95,6 @@ ArbitraryGridDensityProviderFactory::ArbitraryGridDensityProviderFactory(unsigne
{
}
-ArbitraryGridDensityProviderFactory::~ArbitraryGridDensityProviderFactory()
-{
-}
-
AutoPtr<GridDensityProvider> ArbitraryGridDensityProviderFactory::newGridDensityProvider(
OccluderSource &source, const real proscenium[4])
{
diff --git a/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h b/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h
index c2d843742df..ce23d23d7ed 100644
--- a/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h
+++ b/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h
@@ -39,7 +39,6 @@ class ArbitraryGridDensityProvider : public GridDensityProvider {
const GridHelpers::Transform &transform,
unsigned numCells);
ArbitraryGridDensityProvider(OccluderSource &source, unsigned numCells);
- virtual ~ArbitraryGridDensityProvider();
protected:
unsigned numCells;
@@ -51,7 +50,6 @@ class ArbitraryGridDensityProvider : public GridDensityProvider {
class ArbitraryGridDensityProviderFactory : public GridDensityProviderFactory {
public:
ArbitraryGridDensityProviderFactory(unsigned numCells);
- ~ArbitraryGridDensityProviderFactory();
AutoPtr<GridDensityProvider> newGridDensityProvider(OccluderSource &source,
const real proscenium[4]);
diff --git a/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp b/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp
index 1384dc0f78b..9fa8f9dcbc2 100644
--- a/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp
+++ b/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp
@@ -56,10 +56,6 @@ AverageAreaGridDensityProvider::AverageAreaGridDensityProvider(OccluderSource &s
initialize(proscenium, sizeFactor);
}
-AverageAreaGridDensityProvider::~AverageAreaGridDensityProvider()
-{
-}
-
void AverageAreaGridDensityProvider::initialize(const real proscenium[4], real sizeFactor)
{
float prosceniumWidth = (proscenium[1] - proscenium[0]);
@@ -120,10 +116,6 @@ AverageAreaGridDensityProviderFactory::AverageAreaGridDensityProviderFactory(rea
{
}
-AverageAreaGridDensityProviderFactory::~AverageAreaGridDensityProviderFactory()
-{
-}
-
AutoPtr<GridDensityProvider> AverageAreaGridDensityProviderFactory::newGridDensityProvider(
OccluderSource &source, const real proscenium[4])
{
diff --git a/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h b/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h
index 5336cc1ff97..402fc6f210d 100644
--- a/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h
+++ b/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h
@@ -39,7 +39,6 @@ class AverageAreaGridDensityProvider : public GridDensityProvider {
const GridHelpers::Transform &transform,
real sizeFactor);
AverageAreaGridDensityProvider(OccluderSource &source, real sizeFactor);
- virtual ~AverageAreaGridDensityProvider();
private:
void initialize(const real proscenium[4], real sizeFactor);
@@ -48,7 +47,6 @@ class AverageAreaGridDensityProvider : public GridDensityProvider {
class AverageAreaGridDensityProviderFactory : public GridDensityProviderFactory {
public:
AverageAreaGridDensityProviderFactory(real sizeFactor);
- ~AverageAreaGridDensityProviderFactory();
AutoPtr<GridDensityProvider> newGridDensityProvider(OccluderSource &source,
const real proscenium[4]);
diff --git a/source/blender/freestyle/intern/view_map/BoxGrid.cpp b/source/blender/freestyle/intern/view_map/BoxGrid.cpp
index e81291c94bc..ffc0d41bfba 100644
--- a/source/blender/freestyle/intern/view_map/BoxGrid.cpp
+++ b/source/blender/freestyle/intern/view_map/BoxGrid.cpp
@@ -38,14 +38,6 @@ namespace Freestyle {
// Cell
/////////
-BoxGrid::Cell::Cell()
-{
-}
-
-BoxGrid::Cell::~Cell()
-{
-}
-
void BoxGrid::Cell::setDimensions(real x, real y, real sizeX, real sizeY)
{
const real epsilon = 1.0e-06;
@@ -87,10 +79,6 @@ BoxGrid::Iterator::Iterator(BoxGrid &grid, Vec3r &center, real /*epsilon*/)
_current = _cell->faces.begin();
}
-BoxGrid::Iterator::~Iterator()
-{
-}
-
// BoxGrid
/////////////////
@@ -124,9 +112,7 @@ BoxGrid::BoxGrid(OccluderSource &source,
}
}
-BoxGrid::~BoxGrid()
-{
-}
+BoxGrid::~BoxGrid() = default;
void BoxGrid::assignCells(OccluderSource & /*source*/,
GridDensityProvider &density,
@@ -242,10 +228,6 @@ bool BoxGrid::enableQI() const
return _enableQI;
}
-BoxGrid::Transform::Transform()
-{
-}
-
Vec3r BoxGrid::Transform::operator()(const Vec3r &point) const
{
return Vec3r(point[0], point[1], -point[2]);
diff --git a/source/blender/freestyle/intern/view_map/BoxGrid.h b/source/blender/freestyle/intern/view_map/BoxGrid.h
index 581ee0a2340..23085d37a3f 100644
--- a/source/blender/freestyle/intern/view_map/BoxGrid.h
+++ b/source/blender/freestyle/intern/view_map/BoxGrid.h
@@ -73,8 +73,7 @@ class BoxGrid {
// Cell(const Cell& other);
// Cell& operator=(const Cell& other);
- explicit Cell();
- ~Cell();
+ explicit Cell() = default;
static bool compareOccludersByShallowestPoint(const OccluderData *a, const OccluderData *b);
@@ -105,7 +104,6 @@ class BoxGrid {
// epsilon is not used in this class, but other grids with the same interface may need an
// epsilon
explicit Iterator(BoxGrid &grid, Vec3r &center, real epsilon = 1.0e-06);
- ~Iterator();
void initBeforeTarget();
void initAfterTarget();
void nextOccluder();
@@ -134,7 +132,7 @@ class BoxGrid {
class Transform : public GridHelpers::Transform {
public:
- explicit Transform();
+ explicit Transform() = default;
explicit Transform(Transform &other);
Vec3r operator()(const Vec3r &point) const;
};
diff --git a/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp b/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
index a6781bfc8d1..61de0078a8f 100644
--- a/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
+++ b/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
@@ -44,10 +44,6 @@ CulledOccluderSource::CulledOccluderSource(const GridHelpers::Transform &t,
}
}
-CulledOccluderSource::~CulledOccluderSource()
-{
-}
-
bool CulledOccluderSource::testCurrent()
{
if (valid) {
diff --git a/source/blender/freestyle/intern/view_map/CulledOccluderSource.h b/source/blender/freestyle/intern/view_map/CulledOccluderSource.h
index 2bb77bad0f7..16f57f6dc2a 100644
--- a/source/blender/freestyle/intern/view_map/CulledOccluderSource.h
+++ b/source/blender/freestyle/intern/view_map/CulledOccluderSource.h
@@ -36,7 +36,6 @@ class CulledOccluderSource : public OccluderSource {
WingedEdge &we,
ViewMap &viewMap,
bool extensiveFEdgeSearch = true);
- virtual ~CulledOccluderSource();
void cullViewEdges(ViewMap &viewMap, bool extensiveFEdgeSearch);
diff --git a/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp b/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp
index 26a40ee587c..683482d6848 100644
--- a/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp
+++ b/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp
@@ -29,10 +29,6 @@ HeuristicGridDensityProviderFactory::HeuristicGridDensityProviderFactory(real si
{
}
-HeuristicGridDensityProviderFactory::~HeuristicGridDensityProviderFactory()
-{
-}
-
AutoPtr<GridDensityProvider> HeuristicGridDensityProviderFactory::newGridDensityProvider(
OccluderSource &source, const real proscenium[4])
{
diff --git a/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h b/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h
index 0ce62572092..df0f4bd3547 100644
--- a/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h
+++ b/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h
@@ -32,7 +32,6 @@ namespace Freestyle {
class HeuristicGridDensityProviderFactory : public GridDensityProviderFactory {
public:
HeuristicGridDensityProviderFactory(real sizeFactor, unsigned numFaces);
- ~HeuristicGridDensityProviderFactory();
AutoPtr<GridDensityProvider> newGridDensityProvider(OccluderSource &source,
const real proscenium[4]);
diff --git a/source/blender/freestyle/intern/view_map/OccluderSource.cpp b/source/blender/freestyle/intern/view_map/OccluderSource.cpp
index 618fa781fa7..57e6e39b809 100644
--- a/source/blender/freestyle/intern/view_map/OccluderSource.cpp
+++ b/source/blender/freestyle/intern/view_map/OccluderSource.cpp
@@ -33,9 +33,7 @@ OccluderSource::OccluderSource(const GridHelpers::Transform &t, WingedEdge &we)
begin();
}
-OccluderSource::~OccluderSource()
-{
-}
+OccluderSource::~OccluderSource() = default;
void OccluderSource::buildCachedPolygon()
{
diff --git a/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp b/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp
index 7e7f4e14882..b9bf585a9e7 100644
--- a/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp
+++ b/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp
@@ -54,10 +54,6 @@ Pow23GridDensityProvider::Pow23GridDensityProvider(OccluderSource &source, unsig
initialize(proscenium);
}
-Pow23GridDensityProvider::~Pow23GridDensityProvider()
-{
-}
-
void Pow23GridDensityProvider::initialize(const real proscenium[4])
{
float prosceniumWidth = (proscenium[1] - proscenium[0]);
@@ -98,10 +94,6 @@ Pow23GridDensityProviderFactory::Pow23GridDensityProviderFactory(unsigned numFac
{
}
-Pow23GridDensityProviderFactory::~Pow23GridDensityProviderFactory()
-{
-}
-
AutoPtr<GridDensityProvider> Pow23GridDensityProviderFactory::newGridDensityProvider(
OccluderSource &source, const real proscenium[4])
{
diff --git a/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h b/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h
index fec869e0665..d150786b1c7 100644
--- a/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h
+++ b/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h
@@ -37,7 +37,6 @@ class Pow23GridDensityProvider : public GridDensityProvider {
const GridHelpers::Transform &transform,
unsigned numFaces);
Pow23GridDensityProvider(OccluderSource &source, unsigned numFaces);
- virtual ~Pow23GridDensityProvider();
protected:
unsigned numFaces;
@@ -49,7 +48,6 @@ class Pow23GridDensityProvider : public GridDensityProvider {
class Pow23GridDensityProviderFactory : public GridDensityProviderFactory {
public:
Pow23GridDensityProviderFactory(unsigned numFaces);
- ~Pow23GridDensityProviderFactory();
AutoPtr<GridDensityProvider> newGridDensityProvider(OccluderSource &source,
const real proscenium[4]);
diff --git a/source/blender/freestyle/intern/view_map/SphericalGrid.cpp b/source/blender/freestyle/intern/view_map/SphericalGrid.cpp
index db567d6b5ef..f6c57588e35 100644
--- a/source/blender/freestyle/intern/view_map/SphericalGrid.cpp
+++ b/source/blender/freestyle/intern/view_map/SphericalGrid.cpp
@@ -38,14 +38,6 @@ namespace Freestyle {
// Cell
/////////
-SphericalGrid::Cell::Cell()
-{
-}
-
-SphericalGrid::Cell::~Cell()
-{
-}
-
void SphericalGrid::Cell::setDimensions(real x, real y, real sizeX, real sizeY)
{
const real epsilon = 1.0e-06;
@@ -87,10 +79,6 @@ SphericalGrid::Iterator::Iterator(SphericalGrid &grid, Vec3r &center, real /*eps
_current = _cell->faces.begin();
}
-SphericalGrid::Iterator::~Iterator()
-{
-}
-
// SphericalGrid
/////////////////
@@ -121,9 +109,7 @@ SphericalGrid::SphericalGrid(OccluderSource &source,
}
}
-SphericalGrid::~SphericalGrid()
-{
-}
+SphericalGrid::~SphericalGrid() = default;
void SphericalGrid::assignCells(OccluderSource & /*source*/,
GridDensityProvider &density,
@@ -238,10 +224,6 @@ bool SphericalGrid::enableQI() const
return _enableQI;
}
-SphericalGrid::Transform::Transform()
-{
-}
-
Vec3r SphericalGrid::Transform::operator()(const Vec3r &point) const
{
return sphericalProjection(point);
diff --git a/source/blender/freestyle/intern/view_map/SphericalGrid.h b/source/blender/freestyle/intern/view_map/SphericalGrid.h
index 0ef68d073ae..efa3530cb2a 100644
--- a/source/blender/freestyle/intern/view_map/SphericalGrid.h
+++ b/source/blender/freestyle/intern/view_map/SphericalGrid.h
@@ -73,8 +73,7 @@ class SphericalGrid {
// Cell(const Cell& other);
// Cell& operator=(const Cell& other);
- explicit Cell();
- ~Cell();
+ explicit Cell() = default;
static bool compareOccludersByShallowestPoint(const OccluderData *a, const OccluderData *b);
@@ -106,7 +105,6 @@ class SphericalGrid {
// epsilon is not used in this class, but other grids with the same interface may need an
// epsilon
explicit Iterator(SphericalGrid &grid, Vec3r &center, real epsilon = 1.0e-06);
- ~Iterator();
void initBeforeTarget();
void initAfterTarget();
void nextOccluder();
@@ -135,7 +133,7 @@ class SphericalGrid {
class Transform : public GridHelpers::Transform {
public:
- explicit Transform();
+ explicit Transform() = default;
explicit Transform(Transform &other);
Vec3r operator()(const Vec3r &point) const;
static Vec3r sphericalProjection(const Vec3r &M);
diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
index cbb5c730b2b..cd0059f3c21 100644
--- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
@@ -2322,10 +2322,6 @@ struct less_Intersection {
};
struct silhouette_binary_rule : public binary_rule<segment, segment> {
- silhouette_binary_rule()
- {
- }
-
bool operator()(segment &s1, segment &s2) override
{
FEdge *f1 = s1.edge();
diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh
index faf444e91f6..14eab2704e9 100644
--- a/source/blender/functions/FN_cpp_type.hh
+++ b/source/blender/functions/FN_cpp_type.hh
@@ -260,6 +260,11 @@ class CPPType : NonCopyable, NonMovable {
return !(&a == &b);
}
+ /**
+ * Get the `CPPType` that corresponds to a specific static type.
+ * This only works for types that actually implement the template specialization using
+ * `MAKE_CPP_TYPE`.
+ */
template<typename T> static const CPPType &get();
/**
@@ -666,7 +671,7 @@ class CPPType : NonCopyable, NonMovable {
template<typename T> bool is() const
{
- return this == &CPPType::get<T>();
+ return this == &CPPType::get<std::decay_t<T>>();
}
};
@@ -674,6 +679,6 @@ class CPPType : NonCopyable, NonMovable {
/* Utility for allocating an uninitialized buffer for a single value of the given #CPPType. */
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name) \
- blender::DynamicStackBuffer<64, 64> stack_buffer_for_##variable_name(type.size(), \
- type.alignment()); \
+ blender::DynamicStackBuffer<64, 64> stack_buffer_for_##variable_name((type).size(), \
+ (type).alignment()); \
void *variable_name = stack_buffer_for_##variable_name.buffer();
diff --git a/source/blender/functions/FN_generic_pointer.hh b/source/blender/functions/FN_generic_pointer.hh
index 2bd66daa7fe..f88ff09f916 100644
--- a/source/blender/functions/FN_generic_pointer.hh
+++ b/source/blender/functions/FN_generic_pointer.hh
@@ -66,6 +66,16 @@ class GMutablePointer {
return type_ != nullptr && type_->is<T>();
}
+ template<typename T> T relocate_out()
+ {
+ BLI_assert(this->is_type<T>());
+ T value;
+ type_->relocate_to_initialized(data_, &value);
+ data_ = nullptr;
+ type_ = nullptr;
+ return value;
+ }
+
void destruct()
{
BLI_assert(data_ != nullptr);
diff --git a/source/blender/functions/FN_generic_span.hh b/source/blender/functions/FN_generic_span.hh
index 31b67dd3d70..e2c49697ba9 100644
--- a/source/blender/functions/FN_generic_span.hh
+++ b/source/blender/functions/FN_generic_span.hh
@@ -30,7 +30,7 @@ namespace blender::fn {
* A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time.
*/
class GSpan {
- private:
+ protected:
const CPPType *type_;
const void *data_;
int64_t size_;
@@ -85,6 +85,14 @@ class GSpan {
BLI_assert(type_->is<T>());
return Span<T>(static_cast<const T *>(data_), size_);
}
+
+ GSpan slice(const int64_t start, int64_t size) const
+ {
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
+ const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start));
+ return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size);
+ }
};
/**
@@ -92,7 +100,7 @@ class GSpan {
* known at run-time.
*/
class GMutableSpan {
- private:
+ protected:
const CPPType *type_;
void *data_;
int64_t size_;
@@ -153,6 +161,14 @@ class GMutableSpan {
BLI_assert(type_->is<T>());
return MutableSpan<T>(static_cast<T *>(data_), size_);
}
+
+ GMutableSpan slice(const int64_t start, int64_t size) const
+ {
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
+ const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start));
+ return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size);
+ }
};
} // namespace blender::fn
diff --git a/source/blender/functions/FN_generic_value_map.hh b/source/blender/functions/FN_generic_value_map.hh
index 68cb945f1af..4e7fe298874 100644
--- a/source/blender/functions/FN_generic_value_map.hh
+++ b/source/blender/functions/FN_generic_value_map.hh
@@ -93,6 +93,11 @@ template<typename Key> class GValueMap {
return values_.pop_as(key);
}
+ template<typename ForwardKey> GPointer lookup(const ForwardKey &key) const
+ {
+ return values_.lookup_as(key);
+ }
+
/* Remove the value for the given name from the container and remove it. */
template<typename T, typename ForwardKey> T extract(const ForwardKey &key)
{
diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh
index ae6eb8a614f..b02ed471875 100644
--- a/source/blender/functions/FN_generic_vector_array.hh
+++ b/source/blender/functions/FN_generic_vector_array.hh
@@ -123,7 +123,7 @@ template<typename T> class GVectorArray_TypedMutableRef {
void extend(const int64_t index, const VArray<T> &values)
{
- GVArrayForVArray<T> array{values};
+ GVArray_For_VArray<T> array{values};
this->extend(index, array);
}
@@ -134,12 +134,12 @@ template<typename T> class GVectorArray_TypedMutableRef {
};
/* A generic virtual vector array implementation for a `GVectorArray`. */
-class GVVectorArrayForGVectorArray : public GVVectorArray {
+class GVVectorArray_For_GVectorArray : public GVVectorArray {
private:
const GVectorArray &vector_array_;
public:
- GVVectorArrayForGVectorArray(const GVectorArray &vector_array)
+ GVVectorArray_For_GVectorArray(const GVectorArray &vector_array)
: GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array)
{
}
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index c6230730a8d..d530d10b3c8 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -23,12 +23,23 @@
* the data type is only known at runtime.
*/
+#include <optional>
+
#include "BLI_virtual_array.hh"
#include "FN_generic_span.hh"
namespace blender::fn {
+template<typename T> class GVArray_Typed;
+template<typename T> class GVMutableArray_Typed;
+
+class GVArray;
+class GVMutableArray;
+
+using GVArrayPtr = std::unique_ptr<GVArray>;
+using GVMutableArrayPtr = std::unique_ptr<GVMutableArray>;
+
/* A generically typed version of `VArray<T>`. */
class GVArray {
protected:
@@ -86,13 +97,13 @@ class GVArray {
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
* virtual array is not stored as a span internally. */
- GSpan get_span() const
+ GSpan get_internal_span() const
{
BLI_assert(this->is_span());
if (size_ == 0) {
return GSpan(*type_);
}
- return this->get_span_impl();
+ return this->get_internal_span_impl();
}
/* Returns true when the virtual array returns the same value for every index. */
@@ -107,57 +118,151 @@ class GVArray {
/* Copies the value that is used for every element into `r_value`, which is expected to point to
* initialized memory. This invokes undefined behavior if the virtual array would not return the
* same value for every index. */
- void get_single(void *r_value) const
+ void get_internal_single(void *r_value) const
{
BLI_assert(this->is_single());
if (size_ == 1) {
this->get(0, r_value);
+ return;
}
- this->get_single_impl(r_value);
+ this->get_internal_single_impl(r_value);
}
- /* Same as `get_single`, but `r_value` points to initialized memory. */
+ /* Same as `get_internal_single`, but `r_value` points to initialized memory. */
void get_single_to_uninitialized(void *r_value) const
{
type_->construct_default(r_value);
- this->get_single(r_value);
+ this->get_internal_single(r_value);
}
+ void materialize(void *dst) const;
+ void materialize(const IndexMask mask, void *dst) const;
+
+ void materialize_to_uninitialized(void *dst) const;
void materialize_to_uninitialized(const IndexMask mask, void *dst) const;
+ template<typename T> const VArray<T> *try_get_internal_varray() const
+ {
+ BLI_assert(type_->is<T>());
+ return (const VArray<T> *)this->try_get_internal_varray_impl();
+ }
+
+ /* Create a typed virtual array for this generic virtual array. */
+ template<typename T> GVArray_Typed<T> typed() const
+ {
+ return GVArray_Typed<T>(*this);
+ }
+
+ GVArrayPtr shallow_copy() const;
+
protected:
virtual void get_impl(const int64_t index, void *r_value) const;
virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0;
virtual bool is_span_impl() const;
- virtual GSpan get_span_impl() const;
+ virtual GSpan get_internal_span_impl() const;
virtual bool is_single_impl() const;
- virtual void get_single_impl(void *UNUSED(r_value)) const;
+ virtual void get_internal_single_impl(void *UNUSED(r_value)) const;
+
+ virtual void materialize_impl(const IndexMask mask, void *dst) const;
+ virtual void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const;
+
+ virtual const void *try_get_internal_varray_impl() const;
+};
+
+/* Similar to GVArray, but supports changing the elements in the virtual array. */
+class GVMutableArray : public GVArray {
+ public:
+ GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size)
+ {
+ }
+
+ void set_by_copy(const int64_t index, const void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_copy_impl(index, value);
+ }
+
+ void set_by_move(const int64_t index, void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_move_impl(index, value);
+ }
+
+ void set_by_relocate(const int64_t index, void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_relocate_impl(index, value);
+ }
+
+ GMutableSpan get_internal_span()
+ {
+ BLI_assert(this->is_span());
+ GSpan span = static_cast<const GVArray *>(this)->get_internal_span();
+ return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size());
+ }
+
+ template<typename T> VMutableArray<T> *try_get_internal_mutable_varray()
+ {
+ BLI_assert(type_->is<T>());
+ return (VMutableArray<T> *)this->try_get_internal_mutable_varray_impl();
+ }
+
+ /* Create a typed virtual array for this generic virtual array. */
+ template<typename T> GVMutableArray_Typed<T> typed()
+ {
+ return GVMutableArray_Typed<T>(*this);
+ }
+
+ void fill(const void *value);
+
+ /* Copy the values from the source buffer to all elements in the virtual array. */
+ void set_all(const void *src)
+ {
+ this->set_all_impl(src);
+ }
+
+ protected:
+ virtual void set_by_copy_impl(const int64_t index, const void *value);
+ virtual void set_by_relocate_impl(const int64_t index, void *value);
+ virtual void set_by_move_impl(const int64_t index, void *value) = 0;
+
+ virtual void set_all_impl(const void *src);
+
+ virtual void *try_get_internal_mutable_varray_impl();
};
-class GVArrayForGSpan : public GVArray {
+class GVArray_For_GSpan : public GVArray {
protected:
- const void *data_;
+ const void *data_ = nullptr;
const int64_t element_size_;
public:
- GVArrayForGSpan(const GSpan span)
+ GVArray_For_GSpan(const GSpan span)
: GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size())
{
}
protected:
+ GVArray_For_GSpan(const CPPType &type, const int64_t size)
+ : GVArray(type, size), element_size_(type.size())
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override;
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
bool is_span_impl() const override;
- GSpan get_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
};
-class GVArrayForEmpty : public GVArray {
+class GVArray_For_Empty : public GVArray {
public:
- GVArrayForEmpty(const CPPType &type) : GVArray(type, 0)
+ GVArray_For_Empty(const CPPType &type) : GVArray(type, 0)
{
}
@@ -168,108 +273,642 @@ class GVArrayForEmpty : public GVArray {
}
};
-class GVArrayForSingleValueRef : public GVArray {
- private:
- const void *value_;
+class GVMutableArray_For_GMutableSpan : public GVMutableArray {
+ protected:
+ void *data_ = nullptr;
+ const int64_t element_size_;
public:
- GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value)
+ GVMutableArray_For_GMutableSpan(const GMutableSpan span)
+ : GVMutableArray(span.type(), span.size()),
+ data_(span.data()),
+ element_size_(span.type().size())
+ {
+ }
+
+ protected:
+ GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size)
+ : GVMutableArray(type, size), element_size_(type.size())
+ {
+ }
+
+ void get_impl(const int64_t index, void *r_value) const override;
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
+
+ void set_by_copy_impl(const int64_t index, const void *value) override;
+ void set_by_move_impl(const int64_t index, void *value) override;
+ void set_by_relocate_impl(const int64_t index, void *value) override;
+
+ bool is_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
+};
+
+/* Generic virtual array where each element has the same value. The value is not owned. */
+class GVArray_For_SingleValueRef : public GVArray {
+ protected:
+ const void *value_ = nullptr;
+
+ public:
+ GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value)
: GVArray(type, size), value_(value)
{
}
protected:
+ GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size)
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override;
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
bool is_span_impl() const override;
- GSpan get_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
bool is_single_impl() const override;
- void get_single_impl(void *r_value) const override;
+ void get_internal_single_impl(void *r_value) const override;
};
-template<typename T> class GVArrayForVArray : public GVArray {
- private:
- const VArray<T> &array_;
+/* Same as GVArray_For_SingleValueRef, but the value is owned. */
+class GVArray_For_SingleValue : public GVArray_For_SingleValueRef {
+ public:
+ GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value);
+ ~GVArray_For_SingleValue();
+};
+
+/* Used to convert a typed virtual array into a generic one. */
+template<typename T> class GVArray_For_VArray : public GVArray {
+ protected:
+ const VArray<T> *varray_ = nullptr;
public:
- GVArrayForVArray(const VArray<T> &array)
- : GVArray(CPPType::get<T>(), array.size()), array_(array)
+ GVArray_For_VArray(const VArray<T> &varray)
+ : GVArray(CPPType::get<T>(), varray.size()), varray_(&varray)
{
}
protected:
+ GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), size)
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override
{
- *(T *)r_value = array_.get(index);
+ *(T *)r_value = varray_->get(index);
}
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
{
- new (r_value) T(array_.get(index));
+ new (r_value) T(varray_->get(index));
}
bool is_span_impl() const override
{
- return array_.is_span();
+ return varray_->is_span();
}
- GSpan get_span_impl() const override
+ GSpan get_internal_span_impl() const override
{
- return GSpan(array_.get_span());
+ return GSpan(varray_->get_internal_span());
}
bool is_single_impl() const override
{
- return array_.is_single();
+ return varray_->is_single();
}
- void get_single_impl(void *r_value) const override
+ void get_internal_single_impl(void *r_value) const override
{
- *(T *)r_value = array_.get_single();
+ *(T *)r_value = varray_->get_internal_single();
+ }
+
+ void materialize_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ const void *try_get_internal_varray_impl() const override
+ {
+ return varray_;
}
};
-template<typename T> class VArrayForGVArray : public VArray<T> {
- private:
- const GVArray &array_;
+/* Used to convert any generic virtual array into a typed one. */
+template<typename T> class VArray_For_GVArray : public VArray<T> {
+ protected:
+ const GVArray *varray_ = nullptr;
public:
- VArrayForGVArray(const GVArray &array) : VArray<T>(array.size()), array_(array)
+ VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray)
{
- BLI_assert(array_.type().template is<T>());
+ BLI_assert(varray_->type().template is<T>());
}
protected:
+ VArray_For_GVArray(const int64_t size) : VArray<T>(size)
+ {
+ }
+
T get_impl(const int64_t index) const override
{
T value;
- array_.get(index, &value);
+ varray_->get(index, &value);
return value;
}
bool is_span_impl() const override
{
- return array_.is_span();
+ return varray_->is_span();
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
- return array_.get_span().template typed<T>();
+ return varray_->get_internal_span().template typed<T>();
}
bool is_single_impl() const override
{
- return array_.is_single();
+ return varray_->is_single();
}
- T get_single_impl() const override
+ T get_internal_single_impl() const override
{
T value;
- array_.get_single(&value);
+ varray_->get_internal_single(&value);
return value;
}
};
+/* Used to convert an generic mutable virtual array into a typed one. */
+template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> {
+ protected:
+ GVMutableArray *varray_ = nullptr;
+
+ public:
+ VMutableArray_For_GVMutableArray(GVMutableArray &varray)
+ : VMutableArray<T>(varray.size()), varray_(&varray)
+ {
+ BLI_assert(varray.type().template is<T>());
+ }
+
+ VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size)
+ {
+ }
+
+ private:
+ T get_impl(const int64_t index) const override
+ {
+ T value;
+ varray_->get(index, &value);
+ return value;
+ }
+
+ void set_impl(const int64_t index, T value) override
+ {
+ varray_->set_by_relocate(index, &value);
+ }
+
+ bool is_span_impl() const override
+ {
+ return varray_->is_span();
+ }
+
+ Span<T> get_internal_span_impl() const override
+ {
+ return varray_->get_internal_span().template typed<T>();
+ }
+
+ bool is_single_impl() const override
+ {
+ return varray_->is_single();
+ }
+
+ T get_internal_single_impl() const override
+ {
+ T value;
+ varray_->get_internal_single(&value);
+ return value;
+ }
+};
+
+/* Used to convert any typed virtual mutable array into a generic one. */
+template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray {
+ protected:
+ VMutableArray<T> *varray_ = nullptr;
+
+ public:
+ GVMutableArray_For_VMutableArray(VMutableArray<T> &varray)
+ : GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray)
+ {
+ }
+
+ protected:
+ GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size)
+ {
+ }
+
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ *(T *)r_value = varray_->get(index);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ new (r_value) T(varray_->get(index));
+ }
+
+ bool is_span_impl() const override
+ {
+ return varray_->is_span();
+ }
+
+ GSpan get_internal_span_impl() const override
+ {
+ Span<T> span = varray_->get_internal_span();
+ return span;
+ }
+
+ bool is_single_impl() const override
+ {
+ return varray_->is_single();
+ }
+
+ void get_internal_single_impl(void *r_value) const override
+ {
+ *(T *)r_value = varray_->get_internal_single();
+ }
+
+ void set_by_copy_impl(const int64_t index, const void *value) override
+ {
+ const T &value_ = *(const T *)value;
+ varray_->set(index, value_);
+ }
+
+ void set_by_relocate_impl(const int64_t index, void *value) override
+ {
+ T &value_ = *(T *)value;
+ varray_->set(index, std::move(value_));
+ value_.~T();
+ }
+
+ void set_by_move_impl(const int64_t index, void *value) override
+ {
+ T &value_ = *(T *)value;
+ varray_->set(index, std::move(value_));
+ }
+
+ void set_all_impl(const void *src) override
+ {
+ varray_->set_all(Span((T *)src, size_));
+ }
+
+ void materialize_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ const void *try_get_internal_varray_impl() const override
+ {
+ return (const VArray<T> *)varray_;
+ }
+
+ void *try_get_internal_mutable_varray_impl() override
+ {
+ return varray_;
+ }
+};
+
+/* A generic version of VArray_Span. */
+class GVArray_GSpan : public GSpan {
+ private:
+ const GVArray &varray_;
+ void *owned_data_ = nullptr;
+
+ public:
+ GVArray_GSpan(const GVArray &varray);
+ ~GVArray_GSpan();
+};
+
+/* A generic version of VMutableArray_Span. */
+class GVMutableArray_GSpan : public GMutableSpan {
+ private:
+ GVMutableArray &varray_;
+ void *owned_data_ = nullptr;
+ bool save_has_been_called_ = false;
+ bool show_not_saved_warning_ = true;
+
+ public:
+ GVMutableArray_GSpan(GVMutableArray &varray, bool copy_values_to_span = true);
+ ~GVMutableArray_GSpan();
+
+ void save();
+ void disable_not_applied_warning();
+};
+
+/* Similar to GVArray_GSpan, but the resulting span is typed. */
+template<typename T> class GVArray_Span : public Span<T> {
+ private:
+ GVArray_GSpan varray_gspan_;
+
+ public:
+ GVArray_Span(const GVArray &varray) : varray_gspan_(varray)
+ {
+ BLI_assert(varray.type().is<T>());
+ this->data_ = (const T *)varray_gspan_.data();
+ this->size_ = varray_gspan_.size();
+ }
+};
+
+template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> {
+ private:
+ VArrayPtr<T> owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ GVArray_For_OwnedVArray(VArrayPtr<T> varray)
+ : GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> {
+ private:
+ GVArrayPtr owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ VArray_For_OwnedGVArray(GVArrayPtr varray)
+ : VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T>
+class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
+ private:
+ VMutableArrayPtr<T> owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr<T> varray)
+ : GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T>
+class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> {
+ private:
+ GVMutableArrayPtr owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ VMutableArray_For_OwnedGVMutableArray(GVMutableArrayPtr varray)
+ : VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give
+ * the compiler more opportunity to optimize the generic virtual array. */
+template<typename T, typename VArrayT>
+class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> {
+ private:
+ VArrayT embedded_varray_;
+
+ public:
+ template<typename... Args>
+ GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args)
+ : GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
+ {
+ this->varray_ = &embedded_varray_;
+ }
+};
+
+/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */
+template<typename T, typename VMutableArrayT>
+class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
+ private:
+ VMutableArrayT embedded_varray_;
+
+ public:
+ template<typename... Args>
+ GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args)
+ : GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
+ {
+ this->varray_ = &embedded_varray_;
+ }
+};
+
+/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */
+template<typename Container, typename T = typename Container::value_type>
+class GVArray_For_ArrayContainer
+ : public GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>> {
+ public:
+ GVArray_For_ArrayContainer(Container container)
+ : GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>>(
+ container.size(), std::move(container))
+ {
+ }
+};
+
+/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+class GVArray_For_DerivedSpan
+ : public GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>> {
+ public:
+ GVArray_For_DerivedSpan(const Span<StructT> data)
+ : GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
+ data.size(), data)
+ {
+ }
+};
+
+/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+class GVMutableArray_For_DerivedSpan
+ : public GVMutableArray_For_EmbeddedVMutableArray<
+ ElemT,
+ VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> {
+ public:
+ GVMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
+ : GVMutableArray_For_EmbeddedVMutableArray<
+ ElemT,
+ VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(data.size(), data)
+ {
+ }
+};
+
+/* Same as VArray_For_Span, but for a generic virtual array. */
+template<typename T>
+class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>> {
+ public:
+ GVArray_For_Span(const Span<T> data)
+ : GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data)
+ {
+ }
+};
+
+/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */
+template<typename T>
+class GVMutableArray_For_MutableSpan
+ : public GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>> {
+ public:
+ GVMutableArray_For_MutableSpan(const MutableSpan<T> data)
+ : GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(data.size(),
+ data)
+ {
+ }
+};
+
+/**
+ * Utility class to create the "best" typed virtual array for a given generic virtual array.
+ * In most cases we don't just want to use VArray_For_GVArray, because it adds an additional
+ * indirection on element-access that can be avoided in many cases (e.g. when the virtual array is
+ * just a span or single value).
+ *
+ * This is not a virtual array itself, but is used to get a virtual array.
+ */
+template<typename T> class GVArray_Typed {
+ private:
+ const VArray<T> *varray_;
+ /* Of these optional virtual arrays, at most one is constructed at any time. */
+ std::optional<VArray_For_Span<T>> varray_span_;
+ std::optional<VArray_For_Single<T>> varray_single_;
+ std::optional<VArray_For_GVArray<T>> varray_any_;
+ GVArrayPtr owned_gvarray_;
+
+ public:
+ explicit GVArray_Typed(const GVArray &gvarray)
+ {
+ BLI_assert(gvarray.type().is<T>());
+ if (gvarray.is_span()) {
+ const GSpan span = gvarray.get_internal_span();
+ varray_span_.emplace(span.typed<T>());
+ varray_ = &*varray_span_;
+ }
+ else if (gvarray.is_single()) {
+ T single_value;
+ gvarray.get_internal_single(&single_value);
+ varray_single_.emplace(single_value, gvarray.size());
+ varray_ = &*varray_single_;
+ }
+ else if (const VArray<T> *internal_varray = gvarray.try_get_internal_varray<T>()) {
+ varray_ = internal_varray;
+ }
+ else {
+ varray_any_.emplace(gvarray);
+ varray_ = &*varray_any_;
+ }
+ }
+
+ /* Same as the constructor above, but also takes ownership of the passed in virtual array. */
+ explicit GVArray_Typed(GVArrayPtr gvarray) : GVArray_Typed(*gvarray)
+ {
+ owned_gvarray_ = std::move(gvarray);
+ }
+
+ const VArray<T> &operator*() const
+ {
+ return *varray_;
+ }
+
+ const VArray<T> *operator->() const
+ {
+ return varray_;
+ }
+
+ /* Support implicit cast to the typed virtual array for convenience when `varray->typed<T>()` is
+ * used within an expression. */
+ operator const VArray<T> &() const
+ {
+ return *varray_;
+ }
+
+ T operator[](const int64_t index) const
+ {
+ return varray_->get(index);
+ }
+
+ int64_t size() const
+ {
+ return varray_->size();
+ }
+
+ IndexRange index_range() const
+ {
+ return IndexRange(this->size());
+ }
+};
+
+/* Same as GVArray_Typed, but for mutable virtual arrays. */
+template<typename T> class GVMutableArray_Typed {
+ private:
+ VMutableArray<T> *varray_;
+ std::optional<VMutableArray_For_MutableSpan<T>> varray_span_;
+ std::optional<VMutableArray_For_GVMutableArray<T>> varray_any_;
+ GVMutableArrayPtr owned_gvarray_;
+
+ public:
+ explicit GVMutableArray_Typed(GVMutableArray &gvarray)
+ {
+ BLI_assert(gvarray.type().is<T>());
+ if (gvarray.is_span()) {
+ const GMutableSpan span = gvarray.get_internal_span();
+ varray_span_.emplace(span.typed<T>());
+ varray_ = &*varray_span_;
+ }
+ else if (VMutableArray<T> *internal_varray = gvarray.try_get_internal_mutable_varray<T>()) {
+ varray_ = internal_varray;
+ }
+ else {
+ varray_any_.emplace(gvarray);
+ varray_ = &*varray_any_;
+ }
+ }
+
+ explicit GVMutableArray_Typed(GVMutableArrayPtr gvarray) : GVMutableArray_Typed(*gvarray)
+ {
+ owned_gvarray_ = std::move(gvarray);
+ }
+
+ VMutableArray<T> &operator*()
+ {
+ return *varray_;
+ }
+
+ VMutableArray<T> *operator->()
+ {
+ return varray_;
+ }
+
+ operator VMutableArray<T> &()
+ {
+ return *varray_;
+ }
+
+ T operator[](const int64_t index) const
+ {
+ return varray_->get(index);
+ }
+
+ int64_t size() const
+ {
+ return varray_->size();
+ }
+};
+
} // namespace blender::fn
diff --git a/source/blender/functions/FN_generic_virtual_vector_array.hh b/source/blender/functions/FN_generic_virtual_vector_array.hh
index ef3f53b5c25..4155a55a801 100644
--- a/source/blender/functions/FN_generic_virtual_vector_array.hh
+++ b/source/blender/functions/FN_generic_virtual_vector_array.hh
@@ -100,13 +100,13 @@ class GVVectorArray {
}
};
-class GVArrayForGVVectorArrayIndex : public GVArray {
+class GVArray_For_GVVectorArrayIndex : public GVArray {
private:
const GVVectorArray &vector_array_;
const int64_t index_;
public:
- GVArrayForGVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
+ GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
: GVArray(vector_array.type(), vector_array.get_vector_size(index)),
vector_array_(vector_array),
index_(index)
@@ -118,12 +118,12 @@ class GVArrayForGVVectorArrayIndex : public GVArray {
void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override;
};
-class GVVectorArrayForSingleGVArray : public GVVectorArray {
+class GVVectorArray_For_SingleGVArray : public GVVectorArray {
private:
const GVArray &array_;
public:
- GVVectorArrayForSingleGVArray(const GVArray &array, const int64_t size)
+ GVVectorArray_For_SingleGVArray(const GVArray &array, const int64_t size)
: GVVectorArray(array.type(), size), array_(array)
{
}
@@ -137,12 +137,12 @@ class GVVectorArrayForSingleGVArray : public GVVectorArray {
bool is_single_vector_impl() const override;
};
-class GVVectorArrayForSingleGSpan : public GVVectorArray {
+class GVVectorArray_For_SingleGSpan : public GVVectorArray {
private:
const GSpan span_;
public:
- GVVectorArrayForSingleGSpan(const GSpan span, const int64_t size)
+ GVVectorArray_For_SingleGSpan(const GSpan span, const int64_t size)
: GVVectorArray(span.type(), size), span_(span)
{
}
@@ -156,12 +156,12 @@ class GVVectorArrayForSingleGSpan : public GVVectorArray {
bool is_single_vector_impl() const override;
};
-template<typename T> class VVectorArrayForGVVectorArray : public VVectorArray<T> {
+template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray<T> {
private:
const GVVectorArray &vector_array_;
public:
- VVectorArrayForGVVectorArray(const GVVectorArray &vector_array)
+ VVectorArray_For_GVVectorArray(const GVVectorArray &vector_array)
: VVectorArray<T>(vector_array.size()), vector_array_(vector_array)
{
}
diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/functions/FN_multi_function_network_optimization.hh
index 6d0165643ce..96664fa368e 100644
--- a/source/blender/functions/FN_multi_function_network_optimization.hh
+++ b/source/blender/functions/FN_multi_function_network_optimization.hh
@@ -18,12 +18,12 @@
#include "FN_multi_function_network.hh"
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
namespace blender::fn::mf_network_optimization {
void dead_node_removal(MFNetwork &network);
-void constant_folding(MFNetwork &network, ResourceCollector &resources);
+void constant_folding(MFNetwork &network, ResourceScope &scope);
void common_subnetwork_elimination(MFNetwork &network);
} // namespace blender::fn::mf_network_optimization
diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh
index 2d3a8dd650e..e292d11def7 100644
--- a/source/blender/functions/FN_multi_function_params.hh
+++ b/source/blender/functions/FN_multi_function_params.hh
@@ -25,8 +25,9 @@
* the function. `MFParams` is then used inside the called function to access the parameters.
*/
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
+#include "FN_generic_pointer.hh"
#include "FN_generic_vector_array.hh"
#include "FN_generic_virtual_vector_array.hh"
#include "FN_multi_function_signature.hh"
@@ -35,7 +36,7 @@ namespace blender::fn {
class MFParamsBuilder {
private:
- ResourceCollector resources_;
+ ResourceScope scope_;
const MFSignature *signature_;
int64_t min_array_size_;
Vector<const GVArray *> virtual_arrays_;
@@ -55,13 +56,19 @@ class MFParamsBuilder {
template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "")
{
- this->add_readonly_single_input(resources_.construct<GVArrayForSingleValueRef>(
+ this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>(
__func__, CPPType::get<T>(), min_array_size_, value),
expected_name);
}
void add_readonly_single_input(const GSpan span, StringRef expected_name = "")
{
- this->add_readonly_single_input(resources_.construct<GVArrayForGSpan>(__func__, span),
+ this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(__func__, span),
+ expected_name);
+ }
+ void add_readonly_single_input(GPointer value, StringRef expected_name = "")
+ {
+ this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>(
+ __func__, *value.type(), min_array_size_, value.get()),
expected_name);
}
void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "")
@@ -74,7 +81,7 @@ class MFParamsBuilder {
void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "")
{
this->add_readonly_vector_input(
- resources_.construct<GVVectorArrayForGVectorArray>(__func__, vector_array), expected_name);
+ scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name);
}
void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "")
{
@@ -136,9 +143,9 @@ class MFParamsBuilder {
return *vector_arrays_[data_index];
}
- ResourceCollector &resources()
+ ResourceScope &resource_scope()
{
- return resources_;
+ return scope_;
}
private:
@@ -177,7 +184,7 @@ class MFParams {
template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "")
{
const GVArray &array = this->readonly_single_input(param_index, name);
- return builder_->resources_.construct<VArrayForGVArray<T>>(__func__, array);
+ return builder_->scope_.construct<VArray_For_GVArray<T>>(__func__, array);
}
const GVArray &readonly_single_input(int param_index, StringRef name = "")
{
@@ -202,7 +209,7 @@ class MFParams {
const VVectorArray<T> &readonly_vector_input(int param_index, StringRef name = "")
{
const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name);
- return builder_->resources_.construct<VVectorArrayForGVVectorArray<T>>(__func__, vector_array);
+ return builder_->scope_.construct<VVectorArray_For_GVVectorArray<T>>(__func__, vector_array);
}
const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "")
{
diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc
index 53c5def57e9..9c2c1621e23 100644
--- a/source/blender/functions/intern/cpp_types.cc
+++ b/source/blender/functions/intern/cpp_types.cc
@@ -34,8 +34,8 @@ MAKE_CPP_TYPE(int32, int32_t)
MAKE_CPP_TYPE(uint32, uint32_t)
MAKE_CPP_TYPE(uint8, uint8_t)
-MAKE_CPP_TYPE(Color4f, blender::Color4f)
-MAKE_CPP_TYPE(Color4b, blender::Color4b)
+MAKE_CPP_TYPE(ColorGeometry4f, blender::ColorGeometry4f)
+MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b)
MAKE_CPP_TYPE(string, std::string)
diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc
index b3c5517cc43..3335b07e559 100644
--- a/source/blender/functions/intern/generic_vector_array.cc
+++ b/source/blender/functions/intern/generic_vector_array.cc
@@ -60,21 +60,21 @@ void GVectorArray::extend(const int64_t index, const GVArray &values)
void GVectorArray::extend(const int64_t index, const GSpan values)
{
- GVArrayForGSpan varray{values};
+ GVArray_For_GSpan varray{values};
this->extend(index, varray);
}
void GVectorArray::extend(IndexMask mask, const GVVectorArray &values)
{
for (const int i : mask) {
- GVArrayForGVVectorArrayIndex array{values, i};
+ GVArray_For_GVVectorArrayIndex array{values, i};
this->extend(i, array);
}
}
void GVectorArray::extend(IndexMask mask, const GVectorArray &values)
{
- GVVectorArrayForGVectorArray virtual_values{values};
+ GVVectorArray_For_GVectorArray virtual_values{values};
this->extend(mask, virtual_values);
}
diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc
index 9380eb257b2..87dae06ccdc 100644
--- a/source/blender/functions/intern/generic_virtual_array.cc
+++ b/source/blender/functions/intern/generic_virtual_array.cc
@@ -18,8 +18,72 @@
namespace blender::fn {
+/* --------------------------------------------------------------------
+ * GVArray_For_ShallowCopy.
+ */
+
+class GVArray_For_ShallowCopy : public GVArray {
+ private:
+ const GVArray &varray_;
+
+ public:
+ GVArray_For_ShallowCopy(const GVArray &varray)
+ : GVArray(varray.type(), varray.size()), varray_(varray)
+ {
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ varray_.get(index, r_value);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ varray_.get_to_uninitialized(index, r_value);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_.materialize_to_uninitialized(mask, dst);
+ }
+};
+
+/* --------------------------------------------------------------------
+ * GVArray.
+ */
+
+void GVArray::materialize(void *dst) const
+{
+ this->materialize(IndexMask(size_), dst);
+}
+
+void GVArray::materialize(const IndexMask mask, void *dst) const
+{
+ this->materialize_impl(mask, dst);
+}
+
+void GVArray::materialize_impl(const IndexMask mask, void *dst) const
+{
+ for (const int64_t i : mask) {
+ void *elem_dst = POINTER_OFFSET(dst, type_->size() * i);
+ this->get(i, elem_dst);
+ }
+}
+
+void GVArray::materialize_to_uninitialized(void *dst) const
+{
+ this->materialize_to_uninitialized(IndexMask(size_), dst);
+}
+
void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const
{
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_to_uninitialized_impl(mask, dst);
+}
+
+void GVArray::materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const
+{
for (const int64_t i : mask) {
void *elem_dst = POINTER_OFFSET(dst, type_->size() * i);
this->get_to_uninitialized(i, elem_dst);
@@ -37,7 +101,7 @@ bool GVArray::is_span_impl() const
return false;
}
-GSpan GVArray::get_span_impl() const
+GSpan GVArray::get_internal_span_impl() const
{
BLI_assert(false);
return GSpan(*type_);
@@ -48,60 +112,279 @@ bool GVArray::is_single_impl() const
return false;
}
-void GVArray::get_single_impl(void *UNUSED(r_value)) const
+void GVArray::get_internal_single_impl(void *UNUSED(r_value)) const
{
BLI_assert(false);
}
-void GVArrayForGSpan::get_impl(const int64_t index, void *r_value) const
+const void *GVArray::try_get_internal_varray_impl() const
+{
+ return nullptr;
+}
+
+/**
+ * Creates a new `std::unique_ptr<GVArray>` based on this `GVArray`.
+ * The lifetime of the returned virtual array must not be longer than the lifetime of this virtual
+ * array.
+ */
+GVArrayPtr GVArray::shallow_copy() const
+{
+ if (this->is_span()) {
+ return std::make_unique<GVArray_For_GSpan>(this->get_internal_span());
+ }
+ if (this->is_single()) {
+ BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
+ this->get_internal_single(buffer);
+ std::unique_ptr new_varray = std::make_unique<GVArray_For_SingleValue>(*type_, size_, buffer);
+ type_->destruct(buffer);
+ return new_varray;
+ }
+ return std::make_unique<GVArray_For_ShallowCopy>(*this);
+}
+
+/* --------------------------------------------------------------------
+ * GVMutableArray.
+ */
+
+void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value)
+{
+ BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
+ type_->copy_to_uninitialized(value, buffer);
+ this->set_by_move_impl(index, buffer);
+ type_->destruct(buffer);
+}
+
+void GVMutableArray::set_by_relocate_impl(const int64_t index, void *value)
+{
+ this->set_by_move_impl(index, value);
+ type_->destruct(value);
+}
+
+void GVMutableArray::set_all_impl(const void *src)
+{
+ if (this->is_span()) {
+ const GMutableSpan span = this->get_internal_span();
+ type_->copy_to_initialized_n(src, span.data(), size_);
+ }
+ else {
+ for (int64_t i : IndexRange(size_)) {
+ this->set_by_copy(i, POINTER_OFFSET(src, type_->size() * i));
+ }
+ }
+}
+
+void *GVMutableArray::try_get_internal_mutable_varray_impl()
+{
+ return nullptr;
+}
+
+void GVMutableArray::fill(const void *value)
+{
+ if (this->is_span()) {
+ const GMutableSpan span = this->get_internal_span();
+ type_->fill_initialized(value, span.data(), size_);
+ }
+ else {
+ for (int64_t i : IndexRange(size_)) {
+ this->set_by_copy(i, value);
+ }
+ }
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_For_GSpan.
+ */
+
+void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const
{
type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
-void GVArrayForGSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
+void GVArray_For_GSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
{
type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
-bool GVArrayForGSpan::is_span_impl() const
+bool GVArray_For_GSpan::is_span_impl() const
{
return true;
}
-GSpan GVArrayForGSpan::get_span_impl() const
+GSpan GVArray_For_GSpan::get_internal_span_impl() const
{
return GSpan(*type_, data_, size_);
}
-void GVArrayForSingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
+/* --------------------------------------------------------------------
+ * GVMutableArray_For_GMutableSpan.
+ */
+
+void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const
+{
+ type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
+}
+
+void GVMutableArray_For_GMutableSpan::get_to_uninitialized_impl(const int64_t index,
+ void *r_value) const
+{
+ type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_copy_impl(const int64_t index, const void *value)
+{
+ type_->copy_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_move_impl(const int64_t index, void *value)
+{
+ type_->move_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_relocate_impl(const int64_t index, void *value)
+{
+ type_->relocate_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+bool GVMutableArray_For_GMutableSpan::is_span_impl() const
+{
+ return true;
+}
+
+GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const
+{
+ return GSpan(*type_, data_, size_);
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_For_SingleValueRef.
+ */
+
+void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
{
type_->copy_to_initialized(value_, r_value);
}
-void GVArrayForSingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
- void *r_value) const
+void GVArray_For_SingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
+ void *r_value) const
{
type_->copy_to_uninitialized(value_, r_value);
}
-bool GVArrayForSingleValueRef::is_span_impl() const
+bool GVArray_For_SingleValueRef::is_span_impl() const
{
return size_ == 1;
}
-GSpan GVArrayForSingleValueRef::get_span_impl() const
+GSpan GVArray_For_SingleValueRef::get_internal_span_impl() const
{
return GSpan{*type_, value_, 1};
}
-bool GVArrayForSingleValueRef::is_single_impl() const
+bool GVArray_For_SingleValueRef::is_single_impl() const
{
return true;
}
-void GVArrayForSingleValueRef::get_single_impl(void *r_value) const
+void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const
{
type_->copy_to_initialized(value_, r_value);
}
+/* --------------------------------------------------------------------
+ * GVArray_For_SingleValue.
+ */
+
+GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type,
+ const int64_t size,
+ const void *value)
+ : GVArray_For_SingleValueRef(type, size)
+{
+ value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
+ type.copy_to_uninitialized(value, (void *)value_);
+}
+
+GVArray_For_SingleValue::~GVArray_For_SingleValue()
+{
+ type_->destruct((void *)value_);
+ MEM_freeN((void *)value_);
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_GSpan.
+ */
+
+GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray)
+{
+ size_ = varray_.size();
+ if (varray_.is_span()) {
+ data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__);
+ varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_);
+ data_ = owned_data_;
+ }
+}
+
+GVArray_GSpan::~GVArray_GSpan()
+{
+ if (owned_data_ != nullptr) {
+ type_->destruct_n(owned_data_, size_);
+ MEM_freeN(owned_data_);
+ }
+}
+
+/* --------------------------------------------------------------------
+ * GVMutableArray_GSpan.
+ */
+
+GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span)
+ : GMutableSpan(varray.type()), varray_(varray)
+{
+ size_ = varray_.size();
+ if (varray_.is_span()) {
+ data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__);
+ if (copy_values_to_span) {
+ varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_);
+ }
+ else {
+ type_->construct_default_n(owned_data_, size_);
+ }
+ data_ = owned_data_;
+ }
+}
+
+GVMutableArray_GSpan::~GVMutableArray_GSpan()
+{
+ if (show_not_saved_warning_) {
+ if (!save_has_been_called_) {
+ std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n";
+ }
+ }
+ if (owned_data_ != nullptr) {
+ type_->destruct_n(owned_data_, size_);
+ MEM_freeN(owned_data_);
+ }
+}
+
+void GVMutableArray_GSpan::save()
+{
+ save_has_been_called_ = true;
+ if (data_ != owned_data_) {
+ return;
+ }
+ const int64_t element_size = type_->size();
+ for (int64_t i : IndexRange(size_)) {
+ varray_.set_by_copy(i, POINTER_OFFSET(owned_data_, element_size * i));
+ }
+}
+
+void GVMutableArray_GSpan::disable_not_applied_warning()
+{
+ show_not_saved_warning_ = false;
+}
+
} // namespace blender::fn
diff --git a/source/blender/functions/intern/generic_virtual_vector_array.cc b/source/blender/functions/intern/generic_virtual_vector_array.cc
index f6504cee41e..aa3d90883c6 100644
--- a/source/blender/functions/intern/generic_virtual_vector_array.cc
+++ b/source/blender/functions/intern/generic_virtual_vector_array.cc
@@ -18,48 +18,48 @@
namespace blender::fn {
-void GVArrayForGVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
+void GVArray_For_GVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
{
vector_array_.get_vector_element(index_, index_in_vector, r_value);
}
-void GVArrayForGVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
- void *r_value) const
+void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
+ void *r_value) const
{
type_->construct_default(r_value);
vector_array_.get_vector_element(index_, index_in_vector, r_value);
}
-int64_t GVVectorArrayForSingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
+int64_t GVVectorArray_For_SingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
{
return array_.size();
}
-void GVVectorArrayForSingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
- const int64_t index_in_vector,
- void *r_value) const
+void GVVectorArray_For_SingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
+ const int64_t index_in_vector,
+ void *r_value) const
{
array_.get(index_in_vector, r_value);
}
-bool GVVectorArrayForSingleGVArray::is_single_vector_impl() const
+bool GVVectorArray_For_SingleGVArray::is_single_vector_impl() const
{
return true;
}
-int64_t GVVectorArrayForSingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const
+int64_t GVVectorArray_For_SingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const
{
return span_.size();
}
-void GVVectorArrayForSingleGSpan::get_vector_element_impl(const int64_t UNUSED(index),
- const int64_t index_in_vector,
- void *r_value) const
+void GVVectorArray_For_SingleGSpan::get_vector_element_impl(const int64_t UNUSED(index),
+ const int64_t index_in_vector,
+ void *r_value) const
{
type_->copy_to_initialized(span_[index_in_vector], r_value);
}
-bool GVVectorArrayForSingleGSpan::is_single_vector_impl() const
+bool GVVectorArray_For_SingleGSpan::is_single_vector_impl() const
{
return true;
}
diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc
index daa0a42963a..9a0cb0c35ce 100644
--- a/source/blender/functions/intern/multi_function_network_evaluation.cc
+++ b/source/blender/functions/intern/multi_function_network_evaluation.cc
@@ -37,7 +37,7 @@
#include "FN_multi_function_network_evaluation.hh"
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
#include "BLI_stack.hh"
namespace blender::fn {
@@ -70,13 +70,10 @@ class MFNetworkEvaluationStorage {
void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array);
/* Get input buffers for function node evaluations. */
- const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceCollector &resources);
- const GVArray &get_single_input__single(const MFInputSocket &socket,
- ResourceCollector &resources);
- const GVVectorArray &get_vector_input__full(const MFInputSocket &socket,
- ResourceCollector &resources);
- const GVVectorArray &get_vector_input__single(const MFInputSocket &socket,
- ResourceCollector &resources);
+ const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope);
+ const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope);
+ const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope);
+ const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope);
/* Get output buffers for function node evaluations. */
GMutableSpan get_single_output__full(const MFOutputSocket &socket);
@@ -87,16 +84,16 @@ class MFNetworkEvaluationStorage {
/* Get mutable buffers for function node evaluations. */
GMutableSpan get_mutable_single__full(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources);
+ ResourceScope &scope);
GMutableSpan get_mutable_single__single(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources);
+ ResourceScope &scope);
GVectorArray &get_mutable_vector__full(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources);
+ ResourceScope &scope);
GVectorArray &get_mutable_vector__single(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources);
+ ResourceScope &scope);
/* Mark a node as being done with evaluation. This might free temporary buffers that are no
* longer needed. */
@@ -277,20 +274,20 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex
/* The function output would be the same for all elements. Therefore, it is enough to call the
* function only on a single element. This can avoid many duplicate computations. */
MFParamsBuilder params{function, 1};
- ResourceCollector &resources = params.resources();
+ ResourceScope &scope = params.resource_scope();
for (int param_index : function.param_indices()) {
MFParamType param_type = function.param_type(param_index);
switch (param_type.category()) {
case MFParamType::SingleInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__single(socket, resources);
+ const GVArray &values = storage.get_single_input__single(socket, scope);
params.add_readonly_single_input(values);
break;
}
case MFParamType::VectorInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__single(socket, resources);
+ const GVVectorArray &values = storage.get_vector_input__single(socket, scope);
params.add_readonly_vector_input(values);
break;
}
@@ -309,14 +306,14 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex
case MFParamType::SingleMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__single(input, output, resources);
+ GMutableSpan values = storage.get_mutable_single__single(input, output, scope);
params.add_single_mutable(values);
break;
}
case MFParamType::VectorMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__single(input, output, resources);
+ GVectorArray &values = storage.get_mutable_vector__single(input, output, scope);
params.add_vector_mutable(values);
break;
}
@@ -327,20 +324,20 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex
}
else {
MFParamsBuilder params{function, storage.mask().min_array_size()};
- ResourceCollector &resources = params.resources();
+ ResourceScope &scope = params.resource_scope();
for (int param_index : function.param_indices()) {
MFParamType param_type = function.param_type(param_index);
switch (param_type.category()) {
case MFParamType::SingleInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__full(socket, resources);
+ const GVArray &values = storage.get_single_input__full(socket, scope);
params.add_readonly_single_input(values);
break;
}
case MFParamType::VectorInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__full(socket, resources);
+ const GVVectorArray &values = storage.get_vector_input__full(socket, scope);
params.add_readonly_vector_input(values);
break;
}
@@ -359,14 +356,14 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex
case MFParamType::SingleMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__full(input, output, resources);
+ GMutableSpan values = storage.get_mutable_single__full(input, output, scope);
params.add_single_mutable(values);
break;
}
case MFParamType::VectorMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__full(input, output, resources);
+ GVectorArray &values = storage.get_mutable_vector__full(input, output, scope);
params.add_vector_mutable(values);
break;
}
@@ -400,19 +397,19 @@ bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &fu
BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs(
MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const
{
- ResourceCollector resources;
+ ResourceScope scope;
for (const MFInputSocket *socket : remaining_outputs) {
int param_index = inputs_.size() + outputs_.first_index_of(socket);
switch (socket->data_type().category()) {
case MFDataType::Single: {
- const GVArray &values = storage.get_single_input__full(*socket, resources);
+ const GVArray &values = storage.get_single_input__full(*socket, scope);
GMutableSpan output_values = params.uninitialized_single_output(param_index);
values.materialize_to_uninitialized(storage.mask(), output_values.data());
break;
}
case MFDataType::Vector: {
- const GVVectorArray &values = storage.get_vector_input__full(*socket, resources);
+ const GVVectorArray &values = storage.get_vector_input__full(*socket, scope);
GVectorArray &output_values = params.vector_output(param_index);
output_values.extend(storage.mask(), values);
break;
@@ -796,7 +793,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutp
GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
@@ -810,7 +807,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS
if (to_any_value != nullptr) {
BLI_assert(to_any_value->type == ValueType::OutputSingle);
GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
- const GVArray &virtual_array = this->get_single_input__full(input, resources);
+ const GVArray &virtual_array = this->get_single_input__full(input, scope);
virtual_array.materialize_to_uninitialized(mask_, span.data());
return span;
}
@@ -825,7 +822,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS
}
}
- const GVArray &virtual_array = this->get_single_input__full(input, resources);
+ const GVArray &virtual_array = this->get_single_input__full(input, scope);
void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
GMutableSpan new_array_ref(type, new_buffer, min_array_size_);
virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data());
@@ -838,7 +835,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS
GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
@@ -853,7 +850,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu
BLI_assert(to_any_value->type == ValueType::OutputSingle);
GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
BLI_assert(span.size() == 1);
- const GVArray &virtual_array = this->get_single_input__single(input, resources);
+ const GVArray &virtual_array = this->get_single_input__single(input, scope);
virtual_array.get_single_to_uninitialized(span[0]);
return span;
}
@@ -869,7 +866,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu
}
}
- const GVArray &virtual_array = this->get_single_input__single(input, resources);
+ const GVArray &virtual_array = this->get_single_input__single(input, scope);
void *new_buffer = allocator_.allocate(type.size(), type.alignment());
virtual_array.get_single_to_uninitialized(new_buffer);
@@ -883,7 +880,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu
GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
@@ -897,7 +894,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput
if (to_any_value != nullptr) {
BLI_assert(to_any_value->type == ValueType::OutputVector);
GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, resources);
+ const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
vector_array.extend(mask_, virtual_vector_array);
return vector_array;
}
@@ -912,7 +909,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput
}
}
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, resources);
+ const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_);
new_vector_array->extend(mask_, virtual_vector_array);
@@ -926,7 +923,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput
GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
@@ -941,7 +938,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp
BLI_assert(to_any_value->type == ValueType::OutputVector);
GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
BLI_assert(vector_array.size() == 1);
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, resources);
+ const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
vector_array.extend({0}, virtual_vector_array);
return vector_array;
}
@@ -956,7 +953,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp
}
}
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, resources);
+ const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
GVectorArray *new_vector_array = new GVectorArray(base_type, 1);
new_vector_array->extend({0}, virtual_vector_array);
@@ -968,7 +965,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp
}
const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
@@ -977,11 +974,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
if (value->is_single_allocated) {
- return resources.construct<GVArrayForSingleValueRef>(
+ return scope.construct<GVArray_For_SingleValueRef>(
__func__, value->span.type(), min_array_size_, value->span.data());
}
- return resources.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
if (any_value->type == ValueType::InputSingle) {
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
@@ -990,15 +987,15 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS
if (any_value->type == ValueType::OutputSingle) {
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
BLI_assert(value->is_computed);
- return resources.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
BLI_assert(false);
- return resources.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
+ return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
}
const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
@@ -1007,7 +1004,7 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
BLI_assert(value->span.size() == 1);
- return resources.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
if (any_value->type == ValueType::InputSingle) {
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
@@ -1018,15 +1015,15 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
BLI_assert(value->is_computed);
BLI_assert(value->span.size() == 1);
- return resources.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
BLI_assert(false);
- return resources.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
+ return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
- const MFInputSocket &socket, ResourceCollector &resources)
+ const MFInputSocket &socket, ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
@@ -1036,10 +1033,10 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
if (value->vector_array->size() == 1) {
GSpan span = (*value->vector_array)[0];
- return resources.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_);
}
- return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
if (any_value->type == ValueType::InputVector) {
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
@@ -1047,16 +1044,15 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
}
if (any_value->type == ValueType::OutputVector) {
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
- return resources.construct<GVVectorArrayForSingleGSpan>(
- __func__, GSpan(CPPType::get<float>()), 0);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
- const MFInputSocket &socket, ResourceCollector &resources)
+ const MFInputSocket &socket, ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
@@ -1065,7 +1061,7 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
if (any_value->type == ValueType::OwnVector) {
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
BLI_assert(value->vector_array->size() == 1);
- return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
if (any_value->type == ValueType::InputVector) {
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
@@ -1075,12 +1071,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
if (any_value->type == ValueType::OutputVector) {
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
BLI_assert(value->vector_array->size() == 1);
- return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
- return resources.construct<GVVectorArrayForSingleGSpan>(
- __func__, GSpan(CPPType::get<float>()), 0);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
}
/** \} */
diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc
index 6c418dee2c1..4b6b3e81393 100644
--- a/source/blender/functions/intern/multi_function_network_optimization.cc
+++ b/source/blender/functions/intern/multi_function_network_optimization.cc
@@ -219,7 +219,7 @@ static Vector<MFInputSocket *> find_constant_inputs_to_fold(
static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
MFParamsBuilder &params,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
for (int param_index : network_fn.param_indices()) {
MFParamType param_type = network_fn.param_type(param_index);
@@ -229,8 +229,7 @@ static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
case MFDataType::Single: {
/* Allocates memory for a single constant folded value. */
const CPPType &cpp_type = data_type.single_type();
- void *buffer = resources.linear_allocator().allocate(cpp_type.size(),
- cpp_type.alignment());
+ void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment());
GMutableSpan array{cpp_type, buffer, 1};
params.add_uninitialized_single_output(array);
break;
@@ -238,7 +237,7 @@ static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
case MFDataType::Vector: {
/* Allocates memory for a constant folded vector. */
const CPPType &cpp_type = data_type.vector_base_type();
- GVectorArray &vector_array = resources.construct<GVectorArray>(AT, cpp_type, 1);
+ GVectorArray &vector_array = scope.construct<GVectorArray>(AT, cpp_type, 1);
params.add_vector_output(vector_array);
break;
}
@@ -248,7 +247,7 @@ static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn,
MFParamsBuilder &params,
- ResourceCollector &resources,
+ ResourceScope &scope,
MFNetwork &network)
{
Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr};
@@ -264,15 +263,15 @@ static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &
const CPPType &cpp_type = data_type.single_type();
GMutableSpan array = params.computed_array(param_index);
void *buffer = array.data();
- resources.add(buffer, array.type().destruct_cb(), AT);
+ scope.add(buffer, array.type().destruct_cb(), AT);
- constant_fn = &resources.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
+ constant_fn = &scope.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
break;
}
case MFDataType::Vector: {
GVectorArray &vector_array = params.computed_vector_array(param_index);
GSpan array = vector_array[0];
- constant_fn = &resources.construct<CustomMF_GenericConstantArray>(AT, array);
+ constant_fn = &scope.construct<CustomMF_GenericConstantArray>(AT, array);
break;
}
}
@@ -284,17 +283,15 @@ static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &
}
static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes(
- MFNetwork &network,
- Span<const MFInputSocket *> sockets_to_compute,
- ResourceCollector &resources)
+ MFNetwork &network, Span<const MFInputSocket *> sockets_to_compute, ResourceScope &scope)
{
MFNetworkEvaluator network_fn{{}, sockets_to_compute};
MFContextBuilder context;
MFParamsBuilder params{network_fn, 1};
- prepare_params_for_constant_folding(network_fn, params, resources);
+ prepare_params_for_constant_folding(network_fn, params, scope);
network_fn.call({0}, params, context);
- return add_constant_folded_sockets(network_fn, params, resources, network);
+ return add_constant_folded_sockets(network_fn, params, scope, network);
}
class MyClass {
@@ -304,7 +301,7 @@ class MyClass {
/**
* Find function nodes that always output the same value and replace those with constant nodes.
*/
-void constant_folding(MFNetwork &network, ResourceCollector &resources)
+void constant_folding(MFNetwork &network, ResourceScope &scope)
{
Vector<MFDummyNode *> temporary_nodes;
Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes);
@@ -313,7 +310,7 @@ void constant_folding(MFNetwork &network, ResourceCollector &resources)
}
Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes(
- network, inputs_to_fold, resources);
+ network, inputs_to_fold, scope);
for (int i : inputs_to_fold.index_range()) {
MFOutputSocket &original_socket = *inputs_to_fold[i]->origin();
diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc
index 51e116b5983..7b9738e5ca4 100644
--- a/source/blender/functions/tests/FN_multi_function_network_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_network_test.cc
@@ -223,7 +223,7 @@ TEST(multi_function_network, Test2)
Array<int> output_value_2(5, -1);
MFParamsBuilder params(network_fn, 5);
- GVVectorArrayForSingleGSpan inputs_1{input_value_1.as_span(), 5};
+ GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5};
params.add_readonly_vector_input(inputs_1);
params.add_readonly_single_input(&input_value_2);
params.add_vector_output(output_value_1);
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index e3bb0d773a2..f39306ac9d0 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -53,8 +53,9 @@ set(SRC
intern/MOD_gpencilbuild.c
intern/MOD_gpencilcolor.c
intern/MOD_gpencilhook.c
- intern/MOD_gpencillineart.c
intern/MOD_gpencillattice.c
+ intern/MOD_gpencillength.c
+ intern/MOD_gpencillineart.c
intern/MOD_gpencilmirror.c
intern/MOD_gpencilmultiply.c
intern/MOD_gpencilnoise.c
@@ -68,18 +69,19 @@ set(SRC
intern/MOD_gpenciltime.c
intern/MOD_gpenciltint.c
+ MOD_gpencil_lineart.h
MOD_gpencil_modifiertypes.h
intern/MOD_gpencil_ui_common.h
intern/MOD_gpencil_util.h
# Lineart code
- intern/lineart/lineart_ops.c
- intern/lineart/lineart_cpu.c
intern/lineart/lineart_chain.c
+ intern/lineart/lineart_cpu.c
+ intern/lineart/lineart_ops.c
intern/lineart/lineart_util.c
- intern/lineart/lineart_intern.h
intern/lineart/MOD_lineart.h
+ intern/lineart/lineart_intern.h
)
diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
index e6ce7983a0f..f8a28f2e5cb 100644
--- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
+++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
@@ -35,6 +35,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Array;
extern GpencilModifierTypeInfo modifierType_Gpencil_Build;
extern GpencilModifierTypeInfo modifierType_Gpencil_Opacity;
extern GpencilModifierTypeInfo modifierType_Gpencil_Lattice;
+extern GpencilModifierTypeInfo modifierType_Gpencil_Length;
extern GpencilModifierTypeInfo modifierType_Gpencil_Mirror;
extern GpencilModifierTypeInfo modifierType_Gpencil_Smooth;
extern GpencilModifierTypeInfo modifierType_Gpencil_Hook;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
index a156fca5b7b..94285b5032e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
@@ -203,6 +203,20 @@ void gpencil_modifier_curve_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiTemplateCurveMapping(layout, ptr, "curve", 0, false, false, false, false);
}
+void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayout *layout = panel->layout;
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "object", 0, NULL, ICON_CUBE);
+ uiLayout *sub = uiLayoutColumn(layout, true);
+ uiItemR(sub, ptr, "fading_start", 0, NULL, ICON_NONE);
+ uiItemR(sub, ptr, "fading_end", 0, IFACE_("End"), ICON_NONE);
+ uiItemR(layout, ptr, "fading_end_factor", 0, NULL, ICON_NONE);
+}
+
/**
* Draw modifier error message.
*/
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
index 782b36d47ed..75907aaa781 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
@@ -37,6 +37,8 @@ void gpencil_modifier_masking_panel_draw(Panel *panel, bool use_material, bool u
void gpencil_modifier_curve_header_draw(const bContext *C, Panel *panel);
void gpencil_modifier_curve_panel_draw(const bContext *C, Panel *panel);
+void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel);
+
void gpencil_modifier_panel_end(struct uiLayout *layout, PointerRNA *ptr);
struct PointerRNA *gpencil_modifier_panel_get_property_pointers(struct Panel *panel,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index 2dc00079f91..b28a44a0521 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -54,6 +54,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
INIT_GP_TYPE(Build);
INIT_GP_TYPE(Opacity);
INIT_GP_TYPE(Lattice);
+ INIT_GP_TYPE(Length);
INIT_GP_TYPE(Mirror);
INIT_GP_TYPE(Smooth);
INIT_GP_TYPE(Hook);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
index 87d677a5c8a..d9f0fc9bddd 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
@@ -455,8 +455,10 @@ static void generate_geometry(
/* Compute start and end frames for the animation effect
* By default, the upper bound is given by the "maximum length" setting
*/
- float start_frame = gpf->framenum + mmd->start_delay;
- float end_frame = start_frame + mmd->length;
+ float start_frame = is_percentage ? gpf->framenum : gpf->framenum + mmd->start_delay;
+ /* When use percentage don't need a limit in the upper bound, so use a maximum value for the last
+ * frame. */
+ float end_frame = is_percentage ? start_frame + 9999 : start_frame + mmd->length;
if (gpf->next) {
/* Use the next frame or upper bound as end frame, whichever is lower/closer */
@@ -547,6 +549,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
int mode = RNA_enum_get(ptr, "mode");
+ const bool use_percentage = RNA_boolean_get(ptr, "use_percentage");
uiLayoutSetPropSep(layout, true);
@@ -558,16 +561,22 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemS(layout);
uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE);
- uiItemR(layout, ptr, "start_delay", 0, NULL, ICON_NONE);
- uiItemR(layout, ptr, "length", 0, IFACE_("Frames"), ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiLayoutSetActive(row, !use_percentage);
+ uiItemR(row, ptr, "start_delay", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiLayoutSetActive(row, !use_percentage);
+ uiItemR(row, ptr, "length", 0, IFACE_("Frames"), ICON_NONE);
uiItemS(layout);
row = uiLayoutRowWithHeading(layout, true, IFACE_("Use Factor"));
+ uiLayoutSetPropDecorate(row, false);
uiItemR(row, ptr, "use_percentage", 0, "", ICON_NONE);
sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_percentage"));
+ uiLayoutSetActive(sub, use_percentage);
uiItemR(sub, ptr, "percentage_factor", 0, "", ICON_NONE);
+ uiItemDecoratorR(row, ptr, "percentage_factor", 0);
/* Check for incompatible time modifier. */
Object *ob = ob_ptr.data;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
new file mode 100644
index 00000000000..fd94ac92bc3
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
@@ -0,0 +1,223 @@
+/*
+ * ***** 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.
+ *
+ * The Original Code is Copyright (C) 2017, Blender Foundation
+ * This is a new part of Blender
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <stdio.h>
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_defaults.h"
+#include "DNA_gpencil_modifier_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_lib_query.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_screen.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
+#include "MOD_gpencil_util.h"
+
+#include "DEG_depsgraph.h"
+
+static void initData(GpencilModifierData *md)
+{
+ LengthGpencilModifierData *gpmd = (LengthGpencilModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
+
+ MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(LengthGpencilModifierData), modifier);
+}
+
+static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
+{
+ BKE_gpencil_modifier_copydata_generic(md, target);
+}
+
+static bool gpencil_modify_stroke(bGPDstroke *gps,
+ float length,
+ const float overshoot_fac,
+ const short len_mode)
+{
+ bool changed = false;
+ if (length == 0.0f) {
+ return changed;
+ }
+
+ if (length > 0.0f) {
+ BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode);
+ }
+ else {
+ changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode);
+ }
+
+ return changed;
+}
+
+static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke *gps)
+{
+ bool changed = false;
+ const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f :
+ BKE_gpencil_stroke_length(gps, true);
+ if (len < FLT_EPSILON) {
+ return;
+ }
+
+ changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1);
+ changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2);
+
+ if (changed) {
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+}
+
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *UNUSED(depsgraph),
+ GpencilModifierData *md,
+ Object *ob)
+{
+
+ bGPdata *gpd = ob->data;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ applyLength(lmd, gpd, gps);
+ }
+ }
+ }
+}
+
+/* -------------------------------- */
+
+/* Generic "generateStrokes" callback */
+static void deformStroke(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ bGPdata *gpd = ob->data;
+ LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
+ if (is_stroke_affected_by_modifier(ob,
+ lmd->layername,
+ lmd->material,
+ lmd->pass_index,
+ lmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ lmd->flag & GP_LENGTH_INVERT_LAYER,
+ lmd->flag & GP_LENGTH_INVERT_PASS,
+ lmd->flag & GP_LENGTH_INVERT_LAYERPASS,
+ lmd->flag & GP_LENGTH_INVERT_MATERIAL)) {
+ applyLength(lmd, gpd, gps);
+ }
+}
+
+static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ LengthGpencilModifierData *mmd = (LengthGpencilModifierData *)md;
+
+ walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+}
+
+static void panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayoutSetPropSep(layout, true);
+ uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
+
+ uiLayout *col = uiLayoutColumn(layout, true);
+
+ uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE);
+ uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE);
+
+ uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, ptr);
+}
+
+static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Length, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
+GpencilModifierTypeInfo modifierType_Gpencil_Length = {
+ /* name */ "Length",
+ /* structName */ "LengthGpencilModifierData",
+ /* structSize */ sizeof(LengthGpencilModifierData),
+ /* type */ eGpencilModifierTypeType_Gpencil,
+ /* flags */ eGpencilModifierTypeFlag_SupportsEditmode,
+
+ /* copyData */ copyData,
+
+ /* deformStroke */ deformStroke,
+ /* generateStrokes */ NULL,
+ /* bakeModifier */ bakeModifier,
+ /* remapTime */ NULL,
+
+ /* initData */ initData,
+ /* freeData */ NULL,
+ /* isDisabled */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
+};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index 2bab728f5d8..7d2efaebd01 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.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_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -103,7 +104,6 @@ static void generate_strokes_actual(
lmd->transparency_mask,
lmd->thickness,
lmd->opacity,
- lmd->resample_length,
lmd->source_vertex_group,
lmd->vgname,
lmd->flags);
@@ -194,6 +194,27 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return isModifierDisabled(md);
}
+static void add_this_collection(Collection *c,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int mode)
+{
+ if (!c) {
+ return;
+ }
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (c, ob, mode) {
+ if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) {
+ if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) {
+ DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
+ DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
+ }
+ }
+ if (ob->type == OB_EMPTY && (ob->transflag & OB_DUPLICOLLECTION)) {
+ add_this_collection(ob->instance_collection, ctx, mode);
+ }
+ }
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+}
+
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int mode)
@@ -208,16 +229,7 @@ static void updateDepsgraph(GpencilModifierData *md,
ctx->node, lmd->source_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
}
else {
- FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (ctx->scene->master_collection, ob, mode) {
- if (ob->type == OB_MESH || ob->type == OB_MBALL || ob->type == OB_CURVE ||
- ob->type == OB_SURF || ob->type == OB_FONT) {
- if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) {
- DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
- DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
- }
- }
- }
- FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ add_this_collection(ctx->scene->master_collection, ctx, mode);
}
DEG_add_object_relation(
ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
@@ -276,8 +288,25 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(sub, ptr, "crease_threshold", UI_ITEM_R_SLIDER, " ", ICON_NONE);
uiItemPointerR(layout, ptr, "target_layer", &obj_data_ptr, "layers", NULL, ICON_GREASEPENCIL);
- uiItemPointerR(
- layout, ptr, "target_material", &obj_data_ptr, "materials", NULL, ICON_SHADING_TEXTURE);
+
+ /* Material has to be used by grease pencil object already, it was possible to assign materials
+ * without this requirement in earlier versions of blender. */
+ bool material_valid = false;
+ PointerRNA material_ptr = RNA_pointer_get(ptr, "target_material");
+ if (!RNA_pointer_is_null(&material_ptr)) {
+ Material *current_material = material_ptr.data;
+ Object *ob = ob_ptr.data;
+ material_valid = BKE_gpencil_object_material_index_get(ob, current_material) != -1;
+ }
+ uiLayout *row = uiLayoutRow(layout, true);
+ uiLayoutSetRedAlert(row, !material_valid);
+ uiItemPointerR(row,
+ ptr,
+ "target_material",
+ &obj_data_ptr,
+ "materials",
+ NULL,
+ material_valid ? ICON_SHADING_TEXTURE : ICON_ERROR);
uiItemR(layout, ptr, "use_remove_doubles", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE);
@@ -319,7 +348,7 @@ static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel)
if (use_multiple_levels) {
uiLayout *col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "level_start", 0, NULL, ICON_NONE);
- uiItemR(col, ptr, "level_end", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "level_end", 0, IFACE_("End"), ICON_NONE);
}
else {
uiItemR(layout, ptr, "level_start", 0, IFACE_("Level"), ICON_NONE);
@@ -351,7 +380,7 @@ static void transparency_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetPropDecorate(row, false);
- uiLayout *sub = uiLayoutRow(row, true);
+ uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks"));
char text[2] = "0";
PropertyRNA *prop = RNA_struct_find_property(ptr, "use_transparency_mask");
@@ -381,8 +410,6 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "chaining_image_threshold", 0, NULL, ICON_NONE);
- uiItemR(layout, ptr, "resample_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
-
uiItemR(layout, ptr, "split_angle", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
@@ -411,8 +438,6 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemPointerR(
col, ptr, "vertex_group", &ob_ptr, "vertex_groups", IFACE_("Target"), ICON_NONE);
}
-
- uiItemR(col, ptr, "use_soft_selection", 0, NULL, ICON_NONE);
}
static void baking_panel_draw(const bContext *UNUSED(C), Panel *panel)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
index bffb324f07f..cd29a006aae 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
@@ -26,7 +26,11 @@
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "BLI_hash.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
@@ -71,7 +75,7 @@ static void deformStroke(GpencilModifierData *md,
Depsgraph *UNUSED(depsgraph),
Object *ob,
bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
+ bGPDframe *gpf,
bGPDstroke *gps)
{
OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md;
@@ -94,6 +98,46 @@ static void deformStroke(GpencilModifierData *md,
mmd->flag & GP_OFFSET_INVERT_MATERIAL)) {
return;
}
+
+ int seed = mmd->seed;
+ /* Make sure different modifiers get different seeds. */
+ seed += BLI_hash_string(ob->id.name + 2);
+ seed += BLI_hash_string(md->name);
+
+ float rand[3][3];
+ float rand_offset = BLI_hash_int_01(seed);
+
+ /* Get stroke index for random offset. */
+ int rnd_index = BLI_findindex(&gpf->strokes, gps);
+ for (int j = 0; j < 3; j++) {
+ const uint primes[3] = {2, 3, 7};
+ double offset[3] = {0.0f, 0.0f, 0.0f};
+ double r[3];
+ /* To ensure a nice distribution, we use halton sequence and offset using the seed. */
+ BLI_halton_3d(primes, offset, rnd_index, r);
+
+ if ((mmd->flag & GP_OFFSET_UNIFORM_RANDOM_SCALE) && j == 2) {
+ float rand_value;
+ rand_value = fmodf(r[0] * 2.0f - 1.0f + rand_offset, 1.0f);
+ rand_value = fmodf(sin(rand_value * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f);
+ copy_v3_fl(rand[j], rand_value);
+ }
+ else {
+ for (int i = 0; i < 3; i++) {
+ rand[j][i] = fmodf(r[i] * 2.0f - 1.0f + rand_offset, 1.0f);
+ rand[j][i] = fmodf(sin(rand[j][i] * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f);
+ }
+ }
+ }
+ /* Calculate Random matrix. */
+ float mat_rnd[4][4];
+ float rnd_loc[3], rnd_rot[3];
+ float rnd_scale[3] = {1.0f, 1.0f, 1.0f};
+ mul_v3_v3v3(rnd_loc, mmd->rnd_offset, rand[0]);
+ mul_v3_v3v3(rnd_rot, mmd->rnd_rot, rand[1]);
+ madd_v3_v3v3(rnd_scale, mmd->rnd_scale, rand[2]);
+ loc_eul_size_to_mat4(mat_rnd, rnd_loc, rnd_rot, rnd_scale);
+
bGPdata *gpd = ob->data;
for (int i = 0; i < gps->totpoints; i++) {
@@ -106,6 +150,9 @@ static void deformStroke(GpencilModifierData *md,
if (weight < 0.0f) {
continue;
}
+ /* Apply randomness matrix. */
+ mul_m4_v3(mat_rnd, &pt->x);
+
/* Calculate matrix. */
mul_v3_v3fl(loc, mmd->loc, weight);
mul_v3_v3fl(rot, mmd->rot, weight);
@@ -161,6 +208,21 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
gpencil_modifier_panel_end(layout, ptr);
}
+static void random_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayoutSetPropSep(layout, true);
+
+ 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);
+}
+
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
gpencil_modifier_masking_panel_draw(panel, true, true);
@@ -171,6 +233,8 @@ static void panelRegister(ARegionType *region_type)
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Offset, panel_draw);
gpencil_modifier_subpanel_register(
+ region_type, "randomize", "Randomize", NULL, random_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
index c193fc49362..9f03e493ea8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
@@ -47,6 +47,8 @@
#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -84,6 +86,39 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity);
}
+static float give_opacity_fading_factor(OpacityGpencilModifierData *mmd,
+ Object *ob_this,
+ float *pos,
+ bool apply_obmat)
+{
+ float factor_depth = 1.0f;
+
+ if (((mmd->flag & GP_OPACITY_FADING) == 0) || ((mmd->object) == NULL)) {
+ return factor_depth;
+ }
+
+ float gvert[3];
+ if (apply_obmat) {
+ mul_v3_m4v3(gvert, ob_this->obmat, pos);
+ }
+ float dist = len_v3v3(mmd->object->obmat[3], gvert);
+ float fading_max = MAX2(mmd->fading_start, mmd->fading_end);
+ float fading_min = MIN2(mmd->fading_start, mmd->fading_end);
+
+ /* Better with ratiof() function from line art. */
+ if (dist > fading_max) {
+ factor_depth = 0.0f;
+ }
+ else if (dist <= fading_max && dist > fading_min) {
+ factor_depth = (fading_max - dist) / (fading_max - fading_min);
+ }
+ else {
+ factor_depth = 1.0f;
+ }
+
+ return factor_depth;
+}
+
/* opacity strokes */
static void deformStroke(GpencilModifierData *md,
Depsgraph *UNUSED(depsgraph),
@@ -138,6 +173,9 @@ static void deformStroke(GpencilModifierData *md,
factor_curve *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
}
+ float factor_depth = give_opacity_fading_factor(mmd, ob, &pt->x, true);
+ factor_curve = interpf(factor_curve, mmd->fading_end_factor, factor_depth);
+
if (def_nr < 0) {
if (mmd->flag & GP_OPACITY_NORMALIZE) {
pt->strength = factor_curve;
@@ -166,7 +204,9 @@ static void deformStroke(GpencilModifierData *md,
/* Fill using opacity factor. */
if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) {
- gps->fill_opacity_fac = mmd->factor;
+ float factor_depth = give_opacity_fading_factor(mmd, ob, ob->obmat[3], true);
+ gps->fill_opacity_fac = interpf(mmd->factor, mmd->fading_end_factor, factor_depth);
+
CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f);
}
}
@@ -201,6 +241,18 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md;
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+ walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
+}
+
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
+{
+ OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md;
+ if (mmd->object != NULL) {
+ DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier");
+ }
+ DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier");
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -228,6 +280,20 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
gpencil_modifier_panel_end(layout, ptr);
}
+static void fading_header_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE);
+}
+
+static void fading_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_fading_draw(C, panel);
+}
+
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
@@ -266,6 +332,9 @@ static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Opacity, panel_draw);
+
+ gpencil_modifier_subpanel_register(
+ region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type);
PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
@@ -289,7 +358,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Opacity = {
/* initData */ initData,
/* freeData */ freeData,
/* isDisabled */ NULL,
- /* updateDepsgraph */ NULL,
+ /* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
index a13f8d64755..126949cd659 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
@@ -43,6 +43,8 @@
#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -128,6 +130,30 @@ static void deformStroke(GpencilModifierData *md,
}
float curvef = 1.0f;
+
+ float factor_depth = 1.0f;
+
+ if (mmd->flag & GP_THICK_FADING) {
+ if (mmd->object) {
+ float gvert[3];
+ mul_v3_m4v3(gvert, ob->obmat, &pt->x);
+ float dist = len_v3v3(mmd->object->obmat[3], gvert);
+ float fading_max = MAX2(mmd->fading_start, mmd->fading_end);
+ float fading_min = MIN2(mmd->fading_start, mmd->fading_end);
+
+ /* Better with ratiof() function from line art. */
+ if (dist > fading_max) {
+ factor_depth = 0.0f;
+ }
+ else if (dist <= fading_max && dist > fading_min) {
+ factor_depth = (fading_max - dist) / (fading_max - fading_min);
+ }
+ else {
+ factor_depth = 1.0f;
+ }
+ }
+ }
+
if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) {
/* Normalize value to evaluate curve. */
float value = (float)i / (gps->totpoints - 1);
@@ -144,6 +170,11 @@ static void deformStroke(GpencilModifierData *md,
weight *= curvef;
}
+ /* Apply distance fading. */
+ if (mmd->flag & GP_THICK_FADING) {
+ target = interpf(target, mmd->fading_end_factor, factor_depth);
+ }
+
pt->pressure = interpf(target, pt->pressure, weight);
CLAMP_MIN(pt->pressure, 0.0f);
@@ -171,6 +202,32 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md;
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+ walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
+}
+
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
+{
+ ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md;
+ if (mmd->object != NULL) {
+ DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier");
+ }
+ DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier");
+}
+
+static void fading_header_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE);
+}
+
+static void fading_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_fading_draw(C, panel);
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -202,6 +259,8 @@ static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Thick, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type);
PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
gpencil_modifier_subpanel_register(region_type,
@@ -229,7 +288,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Thick = {
/* initData */ initData,
/* freeData */ freeData,
/* isDisabled */ NULL,
- /* updateDepsgraph */ NULL,
+ /* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 1239deb95bb..861085d3e16 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -23,6 +23,7 @@
#pragma once
+#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h" /* Needed here for inline functions. */
#include "BLI_threads.h"
@@ -93,8 +94,8 @@ typedef struct LineartElementLinkNode {
float crease_threshold;
} LineartElementLinkNode;
-typedef struct LineartLineSegment {
- struct LineartLineSegment *next, *prev;
+typedef struct LineartEdgeSegment {
+ struct LineartEdgeSegment *next, *prev;
/** at==0: left at==1: right (this is in 2D projected space) */
double at;
/** Occlusion level after "at" point */
@@ -107,7 +108,7 @@ typedef struct LineartLineSegment {
* enough for most cases.
*/
unsigned char transparency_mask;
-} LineartLineSegment;
+} LineartEdgeSegment;
typedef struct LineartVert {
double gloc[3];
@@ -155,7 +156,7 @@ typedef struct LineartEdge {
/**
* Still need this entry because culled lines will not add to object
- * #LineartElementLinkNode node (known as `reln` internally).
+ * #LineartElementLinkNode node (known as `eln` internally).
*
* TODO: If really need more savings, we can allocate this in a "extended" way too, but we need
* another bit in flags to be able to show the difference.
@@ -163,8 +164,8 @@ typedef struct LineartEdge {
struct Object *object_ref;
} LineartEdge;
-typedef struct LineartLineChain {
- struct LineartLineChain *next, *prev;
+typedef struct LineartEdgeChain {
+ struct LineartEdgeChain *next, *prev;
ListBase chain;
/** Calculated before draw command. */
@@ -179,10 +180,10 @@ typedef struct LineartLineChain {
unsigned char transparency_mask;
struct Object *object_ref;
-} LineartLineChain;
+} LineartEdgeChain;
-typedef struct LineartLineChainItem {
- struct LineartLineChainItem *next, *prev;
+typedef struct LineartEdgeChainItem {
+ struct LineartEdgeChainItem *next, *prev;
/** Need z value for fading */
float pos[3];
/** For restoring position to 3d space */
@@ -192,12 +193,12 @@ typedef struct LineartLineChainItem {
char occlusion;
unsigned char transparency_mask;
size_t index;
-} LineartLineChainItem;
+} LineartEdgeChainItem;
typedef struct LineartChainRegisterEntry {
struct LineartChainRegisterEntry *next, *prev;
- LineartLineChain *rlc;
- LineartLineChainItem *rlci;
+ LineartEdgeChain *ec;
+ LineartEdgeChainItem *eci;
char picked;
/* left/right mark.
@@ -205,6 +206,17 @@ typedef struct LineartChainRegisterEntry {
char is_left;
} LineartChainRegisterEntry;
+enum eLineArtTileRecursiveLimit {
+ /* If tile gets this small, it's already much smaller than a pixel. No need to continue
+ * splitting. */
+ LRT_TILE_RECURSIVE_PERSPECTIVE = 30,
+ /* This is a tried-and-true safe value for high poly models that also needed ortho rendering. */
+ LRT_TILE_RECURSIVE_ORTHO = 10,
+};
+
+#define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100
+#define LRT_TILE_EDGE_COUNT_INITIAL 32
+
typedef struct LineartRenderBuffer {
struct LineartRenderBuffer *prev, *next;
@@ -215,10 +227,16 @@ typedef struct LineartRenderBuffer {
int tile_count_x, tile_count_y;
double width_per_tile, height_per_tile;
double view_projection[4][4];
+ double view[4][4];
struct LineartBoundingArea *initial_bounding_areas;
unsigned int bounding_area_count;
+ /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there
+ * will be a lot of triangles aligned in line which can not be separated by continue subdividing
+ * the tile. So we set a strict limit when using ortho camera. See eLineArtTileRecursiveLimit. */
+ int tile_recursive_level;
+
ListBase vertex_buffer_pointers;
ListBase line_buffer_pointers;
ListBase triangle_buffer_pointers;
@@ -237,31 +255,14 @@ typedef struct LineartRenderBuffer {
int triangle_size;
- unsigned int contour_count;
- unsigned int contour_processed;
- LineartEdge *contour_managed;
- /** A single linked list (cast to #LinkNode). */
- LineartEdge *contours;
-
- unsigned int intersection_count;
- unsigned int intersection_processed;
- LineartEdge *intersection_managed;
- LineartEdge *intersection_lines;
-
- unsigned int crease_count;
- unsigned int crease_processed;
- LineartEdge *crease_managed;
- LineartEdge *crease_lines;
-
- unsigned int material_line_count;
- unsigned int material_processed;
- LineartEdge *material_managed;
- LineartEdge *material_lines;
-
- unsigned int edge_mark_count;
- unsigned int edge_mark_processed;
- LineartEdge *edge_mark_managed;
- LineartEdge *edge_marks;
+ /* Although using ListBase here, LineartEdge is single linked list.
+ * list.last is used to store worker progress along the list.
+ * See lineart_main_occlusion_begin() for more info. */
+ ListBase contour;
+ ListBase intersection;
+ ListBase crease;
+ ListBase material;
+ ListBase edge_mark;
ListBase chains;
@@ -311,7 +312,7 @@ typedef struct LineartRenderBuffer {
#define DBL_TRIANGLE_LIM 1e-8
#define DBL_EDGE_LIM 1e-9
-#define LRT_MEMORY_POOL_64MB (1 << 26)
+#define LRT_MEMORY_POOL_1MB (1 << 20)
typedef enum eLineartTriangleFlags {
LRT_CULL_DONT_CARE = 0,
@@ -322,9 +323,11 @@ typedef enum eLineartTriangleFlags {
LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
} eLineartTriangleFlags;
-/** Controls how many edges a worker thread is processing at one request.
+/**
+ * Controls how many edges a worker thread is processing at one request.
* There's no significant performance impact on choosing different values.
- * Don't make it too small so that the worker thread won't request too many times. */
+ * Don't make it too small so that the worker thread won't request too many times.
+ */
#define LRT_THREAD_EDGE_COUNT 1000
typedef struct LineartRenderTaskInfo {
@@ -332,23 +335,51 @@ typedef struct LineartRenderTaskInfo {
int thread_id;
- LineartEdge *contour;
- LineartEdge *contour_end;
-
- LineartEdge *intersection;
- LineartEdge *intersection_end;
-
- LineartEdge *crease;
- LineartEdge *crease_end;
-
- LineartEdge *material;
- LineartEdge *material_end;
-
- LineartEdge *edge_mark;
- LineartEdge *edge_mark_end;
+ /* These lists only denote the part of the main edge list that the thread should iterate over.
+ * Be careful to not iterate outside of these bounds as it is not thread safe to do so. */
+ ListBase contour;
+ ListBase intersection;
+ ListBase crease;
+ ListBase material;
+ ListBase edge_mark;
} LineartRenderTaskInfo;
+struct BMesh;
+
+typedef struct LineartObjectInfo {
+ struct LineartObjectInfo *next;
+ struct Object *original_ob;
+ struct Mesh *original_me;
+ double model_view_proj[4][4];
+ double model_view[4][4];
+ double normal[4][4];
+ LineartElementLinkNode *v_reln;
+ int usage;
+ int global_i_offset;
+
+ bool free_use_mesh;
+
+ /* Threads will add lines inside here, when all threads are done, we combine those into the
+ * ones in LineartRenderBuffer. */
+ ListBase contour;
+ ListBase intersection;
+ ListBase crease;
+ ListBase material;
+ ListBase edge_mark;
+ ListBase floating;
+
+} LineartObjectInfo;
+
+typedef struct LineartObjectLoadTaskInfo {
+ struct LineartRenderBuffer *rb;
+ struct Depsgraph *dg;
+ /* LinkNode styled list */
+ LineartObjectInfo *pending;
+ /* Used to spread the load across several threads. This can not overflow. */
+ long unsigned int total_faces;
+} LineartObjectLoadTaskInfo;
+
/**
* Bounding area diagram:
* \code{.txt}
@@ -385,10 +416,14 @@ typedef struct LineartBoundingArea {
ListBase up;
ListBase bp;
- short triangle_count;
+ int16_t triangle_count;
+ int16_t max_triangle_count;
+ int16_t line_count;
+ int16_t max_line_count;
- ListBase linked_triangles;
- ListBase linked_lines;
+ /* Use array for speeding up multiple accesses. */
+ struct LineartTriangle **linked_triangles;
+ struct LineartEdge **linked_lines;
/** Reserved for image space reduction && multi-thread chaining. */
ListBase linked_chains;
@@ -527,7 +562,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb);
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold);
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad);
-int MOD_lineart_chain_count(const LineartLineChain *rlc);
+int MOD_lineart_chain_count(const LineartEdgeChain *ec);
void MOD_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb);
bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph,
@@ -543,7 +578,6 @@ LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, doub
struct bGPDlayer;
struct bGPDframe;
-struct GpencilModifierData;
void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
struct Depsgraph *depsgraph,
@@ -560,11 +594,10 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
unsigned char transparency_mask,
short thickness,
float opacity,
- float resample_length,
const char *source_vgname,
const char *vgname,
int modifier_flags);
-float MOD_lineart_chain_compute_length(LineartLineChain *rlc);
+float MOD_lineart_chain_compute_length(LineartEdgeChain *ec);
void ED_operatortypes_lineart(void);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index 464316b6a10..25c4e959ac1 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -31,17 +31,17 @@
#include <math.h>
-#define LRT_OTHER_RV(e, rv) ((rv) == (e)->v1 ? (e)->v2 : ((rv) == (e)->v2 ? (e)->v1 : NULL))
+#define LRT_OTHER_VERT(e, vt) ((vt) == (e)->v1 ? (e)->v2 : ((vt) == (e)->v2 ? (e)->v1 : NULL))
/* Get a connected line, only for lines who has the exact given vert, or (in the case of
* intersection lines) who has a vert that has the exact same position. */
static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
- LineartVert *rv,
- LineartVert **new_rv,
+ LineartVert *vt,
+ LineartVert **new_vt,
int match_flag)
{
- LISTBASE_FOREACH (LinkData *, lip, &ba->linked_lines) {
- LineartEdge *n_e = lip->data;
+ for (int i = 0; i < ba->line_count; i++) {
+ LineartEdge *n_e = ba->linked_lines[i];
if ((!(n_e->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (n_e->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) {
continue;
@@ -51,18 +51,18 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
continue;
}
- *new_rv = LRT_OTHER_RV(n_e, rv);
- if (*new_rv) {
+ *new_vt = LRT_OTHER_VERT(n_e, vt);
+ if (*new_vt) {
return n_e;
}
if (n_e->flags & LRT_EDGE_FLAG_INTERSECTION) {
- if (rv->fbcoord[0] == n_e->v1->fbcoord[0] && rv->fbcoord[1] == n_e->v1->fbcoord[1]) {
- *new_rv = LRT_OTHER_RV(n_e, n_e->v1);
+ if (vt->fbcoord[0] == n_e->v1->fbcoord[0] && vt->fbcoord[1] == n_e->v1->fbcoord[1]) {
+ *new_vt = LRT_OTHER_VERT(n_e, n_e->v1);
return n_e;
}
- if (rv->fbcoord[0] == n_e->v2->fbcoord[0] && rv->fbcoord[1] == n_e->v2->fbcoord[1]) {
- *new_rv = LRT_OTHER_RV(n_e, n_e->v2);
+ if (vt->fbcoord[0] == n_e->v2->fbcoord[0] && vt->fbcoord[1] == n_e->v2->fbcoord[1]) {
+ *new_vt = LRT_OTHER_VERT(n_e, n_e->v2);
return n_e;
}
}
@@ -71,33 +71,33 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
return NULL;
}
-static LineartLineChain *lineart_chain_create(LineartRenderBuffer *rb)
+static LineartEdgeChain *lineart_chain_create(LineartRenderBuffer *rb)
{
- LineartLineChain *rlc;
- rlc = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChain));
+ LineartEdgeChain *ec;
+ ec = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChain));
- BLI_addtail(&rb->chains, rlc);
+ BLI_addtail(&rb->chains, ec);
- return rlc;
+ return ec;
}
-static bool lineart_point_overlapping(LineartLineChainItem *rlci,
+static bool lineart_point_overlapping(LineartEdgeChainItem *eci,
float x,
float y,
double threshold)
{
- if (!rlci) {
+ if (!eci) {
return false;
}
- if (((rlci->pos[0] + threshold) >= x) && ((rlci->pos[0] - threshold) <= x) &&
- ((rlci->pos[1] + threshold) >= y) && ((rlci->pos[1] - threshold) <= y)) {
+ if (((eci->pos[0] + threshold) >= x) && ((eci->pos[0] - threshold) <= x) &&
+ ((eci->pos[1] + threshold) >= y) && ((eci->pos[1] - threshold) <= y)) {
return true;
}
return false;
}
-static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
- LineartLineChain *rlc,
+static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
+ LineartEdgeChain *ec,
float *fbcoord,
float *gpos,
float *normal,
@@ -106,35 +106,35 @@ static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
unsigned char transparency_mask,
size_t index)
{
- LineartLineChainItem *rlci;
+ LineartEdgeChainItem *eci;
- if (lineart_point_overlapping(rlc->chain.last, fbcoord[0], fbcoord[1], 1e-5)) {
+ if (lineart_point_overlapping(ec->chain.last, fbcoord[0], fbcoord[1], 1e-5)) {
/* Because the new chain point is overlapping, just replace the type and occlusion level of the
* current point. This makes it so that the line to the point after this one has the correct
* type and level. */
- LineartLineChainItem *old_rlci = rlc->chain.last;
+ LineartEdgeChainItem *old_rlci = ec->chain.last;
old_rlci->line_type = type;
old_rlci->occlusion = level;
old_rlci->transparency_mask = transparency_mask;
return old_rlci;
}
- rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChainItem));
+ eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem));
- copy_v2_v2(rlci->pos, fbcoord);
- copy_v3_v3(rlci->gpos, gpos);
- rlci->index = index;
- copy_v3_v3(rlci->normal, normal);
- rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
- rlci->occlusion = level;
- rlci->transparency_mask = transparency_mask;
- BLI_addtail(&rlc->chain, rlci);
+ copy_v2_v2(eci->pos, fbcoord);
+ copy_v3_v3(eci->gpos, gpos);
+ eci->index = index;
+ copy_v3_v3(eci->normal, normal);
+ eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
+ eci->occlusion = level;
+ eci->transparency_mask = transparency_mask;
+ BLI_addtail(&ec->chain, eci);
- return rlci;
+ return eci;
}
-static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb,
- LineartLineChain *rlc,
+static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb,
+ LineartEdgeChain *ec,
float *fbcoord,
float *gpos,
float *normal,
@@ -143,32 +143,32 @@ static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb
unsigned char transparency_mask,
size_t index)
{
- LineartLineChainItem *rlci;
+ LineartEdgeChainItem *eci;
- if (lineart_point_overlapping(rlc->chain.first, fbcoord[0], fbcoord[1], 1e-5)) {
- return rlc->chain.first;
+ if (lineart_point_overlapping(ec->chain.first, fbcoord[0], fbcoord[1], 1e-5)) {
+ return ec->chain.first;
}
- rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChainItem));
+ eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem));
- copy_v2_v2(rlci->pos, fbcoord);
- copy_v3_v3(rlci->gpos, gpos);
- rlci->index = index;
- copy_v3_v3(rlci->normal, normal);
- rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
- rlci->occlusion = level;
- rlci->transparency_mask = transparency_mask;
- BLI_addhead(&rlc->chain, rlci);
+ copy_v2_v2(eci->pos, fbcoord);
+ copy_v3_v3(eci->gpos, gpos);
+ eci->index = index;
+ copy_v3_v3(eci->normal, normal);
+ eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
+ eci->occlusion = level;
+ eci->transparency_mask = transparency_mask;
+ BLI_addhead(&ec->chain, eci);
- return rlci;
+ return eci;
}
void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
{
- LineartLineChain *rlc;
- LineartLineChainItem *rlci;
+ LineartEdgeChain *ec;
+ LineartEdgeChainItem *eci;
LineartBoundingArea *ba;
- LineartLineSegment *rls;
+ LineartEdgeSegment *es;
int last_occlusion;
unsigned char last_transparency;
/* Used when converting from double. */
@@ -192,14 +192,14 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
- rlc = lineart_chain_create(rb);
+ ec = lineart_chain_create(rb);
/* One chain can only have one object_ref,
* so we assign it based on the first segment we found. */
- rlc->object_ref = e->object_ref;
+ ec->object_ref = e->object_ref;
- LineartEdge *new_e = e;
- LineartVert *new_rv;
+ LineartEdge *new_e;
+ LineartVert *new_vt;
float N[3] = {0};
if (e->t1) {
@@ -218,19 +218,19 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
/* Step 1: grow left. */
ba = MOD_lineart_get_bounding_area(rb, e->v1->fbcoord[0], e->v1->fbcoord[1]);
- new_rv = e->v1;
- rls = e->segments.first;
- VERT_COORD_TO_FLOAT(new_rv);
+ new_vt = e->v1;
+ es = e->segments.first;
+ VERT_COORD_TO_FLOAT(new_vt);
lineart_chain_prepend_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
e->flags,
- rls->occlusion,
- rls->transparency_mask,
+ es->occlusion,
+ es->transparency_mask,
e->v1_obindex);
- while (ba && (new_e = lineart_line_get_connected(ba, new_rv, &new_rv, e->flags))) {
+ while (ba && (new_e = lineart_line_get_connected(ba, new_vt, &new_vt, e->flags))) {
new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
if (new_e->t1 || new_e->t2) {
@@ -248,41 +248,41 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
normalize_v3(N);
}
- if (new_rv == new_e->v1) {
- for (rls = new_e->segments.last; rls; rls = rls->prev) {
+ if (new_vt == new_e->v1) {
+ for (es = new_e->segments.last; es; es = es->prev) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
new_e->flags,
- rls->occlusion,
- rls->transparency_mask,
+ es->occlusion,
+ es->transparency_mask,
new_e->v1_obindex);
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
}
}
- else if (new_rv == new_e->v2) {
- rls = new_e->segments.first;
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
- rls = rls->next;
- for (; rls; rls = rls->next) {
+ else if (new_vt == new_e->v2) {
+ es = new_e->segments.first;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
+ es = es->next;
+ for (; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -290,12 +290,12 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
last_occlusion,
last_transparency,
new_e->v2_obindex);
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
}
VERT_COORD_TO_FLOAT(new_e->v2);
lineart_chain_prepend_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -304,7 +304,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
last_transparency,
new_e->v2_obindex);
}
- ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
+ ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]);
}
/* Restore normal value. */
@@ -324,31 +324,31 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
}
/* Step 2: Adding all cuts from the given line, so we can continue connecting the right side
* of the line. */
- rls = e->segments.first;
- last_occlusion = ((LineartLineSegment *)rls)->occlusion;
- last_transparency = ((LineartLineSegment *)rls)->transparency_mask;
- for (rls = rls->next; rls; rls = rls->next) {
+ es = e->segments.first;
+ last_occlusion = ((LineartEdgeSegment *)es)->occlusion;
+ last_transparency = ((LineartEdgeSegment *)es)->transparency_mask;
+ for (es = es->next; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = e->v1->fbcoord, *rfb = e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
e->flags,
- rls->occlusion,
- rls->transparency_mask,
+ es->occlusion,
+ es->transparency_mask,
e->v1_obindex);
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
}
VERT_COORD_TO_FLOAT(e->v2)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -359,8 +359,8 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
/* Step 3: grow right. */
ba = MOD_lineart_get_bounding_area(rb, e->v2->fbcoord[0], e->v2->fbcoord[1]);
- new_rv = e->v2;
- while (ba && (new_e = lineart_line_get_connected(ba, new_rv, &new_rv, e->flags))) {
+ new_vt = e->v2;
+ while (ba && (new_e = lineart_line_get_connected(ba, new_vt, &new_vt, e->flags))) {
new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
if (new_e->t1 || new_e->t2) {
@@ -379,27 +379,27 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
}
/* Fix leading vertex type. */
- rlci = rlc->chain.last;
- rlci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE;
+ eci = ec->chain.last;
+ eci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE;
- if (new_rv == new_e->v1) {
- rls = new_e->segments.last;
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ if (new_vt == new_e->v1) {
+ es = new_e->segments.last;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
/* Fix leading vertex occlusion. */
- rlci->occlusion = last_occlusion;
- rlci->transparency_mask = last_transparency;
- for (rls = new_e->segments.last; rls; rls = rls->prev) {
+ eci->occlusion = last_occlusion;
+ eci->transparency_mask = last_transparency;
+ for (es = new_e->segments.last; es; es = es->prev) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
- last_occlusion = rls->prev ? rls->prev->occlusion : last_occlusion;
- last_transparency = rls->prev ? rls->prev->transparency_mask : last_transparency;
+ last_occlusion = es->prev ? es->prev->occlusion : last_occlusion;
+ last_transparency = es->prev ? es->prev->transparency_mask : last_transparency;
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -409,35 +409,35 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
new_e->v1_obindex);
}
}
- else if (new_rv == new_e->v2) {
- rls = new_e->segments.first;
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
- rlci->occlusion = last_occlusion;
- rlci->transparency_mask = last_transparency;
- rls = rls->next;
- for (; rls; rls = rls->next) {
+ else if (new_vt == new_e->v2) {
+ es = new_e->segments.first;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
+ eci->occlusion = last_occlusion;
+ eci->transparency_mask = last_transparency;
+ es = es->next;
+ for (; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
new_e->flags,
- rls->occlusion,
- rls->transparency_mask,
+ es->occlusion,
+ es->transparency_mask,
new_e->v2_obindex);
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
}
VERT_COORD_TO_FLOAT(new_e->v2)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -446,57 +446,57 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
last_transparency,
new_e->v2_obindex);
}
- ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
+ ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]);
}
if (rb->fuzzy_everything) {
- rlc->type = LRT_EDGE_FLAG_CONTOUR;
+ ec->type = LRT_EDGE_FLAG_CONTOUR;
}
else {
- rlc->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE);
+ ec->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE);
}
}
LRT_ITER_ALL_LINES_END
}
-static LineartBoundingArea *lineart_bounding_area_get_rlci_recursive(LineartRenderBuffer *rb,
- LineartBoundingArea *root,
- LineartLineChainItem *rlci)
+static LineartBoundingArea *lineart_bounding_area_get_eci_recursive(LineartRenderBuffer *rb,
+ LineartBoundingArea *root,
+ LineartEdgeChainItem *eci)
{
if (root->child == NULL) {
return root;
}
LineartBoundingArea *ch = root->child;
-#define IN_BOUND(ba, rlci) \
- ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1]
+#define IN_BOUND(ba, eci) \
+ ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1]
- if (IN_BOUND(ch[0], rlci)) {
- return lineart_bounding_area_get_rlci_recursive(rb, &ch[0], rlci);
+ if (IN_BOUND(ch[0], eci)) {
+ return lineart_bounding_area_get_eci_recursive(rb, &ch[0], eci);
}
- if (IN_BOUND(ch[1], rlci)) {
- return lineart_bounding_area_get_rlci_recursive(rb, &ch[1], rlci);
+ if (IN_BOUND(ch[1], eci)) {
+ return lineart_bounding_area_get_eci_recursive(rb, &ch[1], eci);
}
- if (IN_BOUND(ch[2], rlci)) {
- return lineart_bounding_area_get_rlci_recursive(rb, &ch[2], rlci);
+ if (IN_BOUND(ch[2], eci)) {
+ return lineart_bounding_area_get_eci_recursive(rb, &ch[2], eci);
}
- if (IN_BOUND(ch[3], rlci)) {
- return lineart_bounding_area_get_rlci_recursive(rb, &ch[3], rlci);
+ if (IN_BOUND(ch[3], eci)) {
+ return lineart_bounding_area_get_eci_recursive(rb, &ch[3], eci);
}
#undef IN_BOUND
return NULL;
}
static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuffer *rb,
- LineartLineChainItem *rlci)
+ LineartEdgeChainItem *eci)
{
- if (!rlci) {
+ if (!eci) {
return NULL;
}
- LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, rlci->pos[0], rlci->pos[1]);
+ LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, eci->pos[0], eci->pos[1]);
if (root == NULL) {
return NULL;
}
- return lineart_bounding_area_get_rlci_recursive(rb, root, rlci);
+ return lineart_bounding_area_get_eci_recursive(rb, root, eci);
}
/**
@@ -507,61 +507,61 @@ static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuf
*/
static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb,
LineartBoundingArea *root,
- LineartLineChain *rlc,
- LineartLineChainItem *rlci)
+ LineartEdgeChain *ec,
+ LineartEdgeChainItem *eci)
{
if (root->child == NULL) {
LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized(
- &root->linked_chains, &rb->render_data_pool, rlc, sizeof(LineartChainRegisterEntry));
+ &root->linked_chains, &rb->render_data_pool, ec, sizeof(LineartChainRegisterEntry));
- cre->rlci = rlci;
+ cre->eci = eci;
- if (rlci == rlc->chain.first) {
+ if (eci == ec->chain.first) {
cre->is_left = 1;
}
}
else {
LineartBoundingArea *ch = root->child;
-#define IN_BOUND(ba, rlci) \
- ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1]
+#define IN_BOUND(ba, eci) \
+ ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1]
- if (IN_BOUND(ch[0], rlci)) {
- lineart_bounding_area_link_point_recursive(rb, &ch[0], rlc, rlci);
+ if (IN_BOUND(ch[0], eci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[0], ec, eci);
}
- else if (IN_BOUND(ch[1], rlci)) {
- lineart_bounding_area_link_point_recursive(rb, &ch[1], rlc, rlci);
+ else if (IN_BOUND(ch[1], eci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[1], ec, eci);
}
- else if (IN_BOUND(ch[2], rlci)) {
- lineart_bounding_area_link_point_recursive(rb, &ch[2], rlc, rlci);
+ else if (IN_BOUND(ch[2], eci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[2], ec, eci);
}
- else if (IN_BOUND(ch[3], rlci)) {
- lineart_bounding_area_link_point_recursive(rb, &ch[3], rlc, rlci);
+ else if (IN_BOUND(ch[3], eci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[3], ec, eci);
}
#undef IN_BOUND
}
}
-static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartLineChain *rlc)
+static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartEdgeChain *ec)
{
- LineartLineChainItem *pl = rlc->chain.first;
- LineartLineChainItem *pr = rlc->chain.last;
+ LineartEdgeChainItem *pl = ec->chain.first;
+ LineartEdgeChainItem *pr = ec->chain.last;
LineartBoundingArea *ba1 = MOD_lineart_get_parent_bounding_area(rb, pl->pos[0], pl->pos[1]);
LineartBoundingArea *ba2 = MOD_lineart_get_parent_bounding_area(rb, pr->pos[0], pr->pos[1]);
if (ba1) {
- lineart_bounding_area_link_point_recursive(rb, ba1, rlc, pl);
+ lineart_bounding_area_link_point_recursive(rb, ba1, ec, pl);
}
if (ba2) {
- lineart_bounding_area_link_point_recursive(rb, ba2, rlc, pr);
+ lineart_bounding_area_link_point_recursive(rb, ba2, ec, pr);
}
}
void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
{
- LineartLineChain *rlc, *new_rlc;
- LineartLineChainItem *rlci, *next_rlci;
+ LineartEdgeChain *ec, *new_rlc;
+ LineartEdgeChainItem *eci, *next_rlci;
ListBase swap = {0};
swap.first = rb->chains.first;
@@ -569,59 +569,59 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
rb->chains.last = rb->chains.first = NULL;
- while ((rlc = BLI_pophead(&swap)) != NULL) {
- rlc->next = rlc->prev = NULL;
- BLI_addtail(&rb->chains, rlc);
- LineartLineChainItem *first_rlci = (LineartLineChainItem *)rlc->chain.first;
+ while ((ec = BLI_pophead(&swap)) != NULL) {
+ ec->next = ec->prev = NULL;
+ BLI_addtail(&rb->chains, ec);
+ LineartEdgeChainItem *first_rlci = (LineartEdgeChainItem *)ec->chain.first;
int fixed_occ = first_rlci->occlusion;
unsigned char fixed_mask = first_rlci->transparency_mask;
- rlc->level = fixed_occ;
- rlc->transparency_mask = fixed_mask;
- for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
- next_rlci = rlci->next;
- if (rlci->occlusion != fixed_occ || rlci->transparency_mask != fixed_mask) {
+ ec->level = fixed_occ;
+ ec->transparency_mask = fixed_mask;
+ for (eci = first_rlci->next; eci; eci = next_rlci) {
+ next_rlci = eci->next;
+ if (eci->occlusion != fixed_occ || eci->transparency_mask != fixed_mask) {
if (next_rlci) {
- if (lineart_point_overlapping(next_rlci, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ if (lineart_point_overlapping(next_rlci, eci->pos[0], eci->pos[1], 1e-5)) {
continue;
}
}
else {
/* Set the same occlusion level for the end vertex, so when further connection is needed
* the backwards occlusion info is also correct. */
- rlci->occlusion = fixed_occ;
- rlci->transparency_mask = fixed_mask;
+ eci->occlusion = fixed_occ;
+ eci->transparency_mask = fixed_mask;
/* No need to split at the last point anyway. */
break;
}
new_rlc = lineart_chain_create(rb);
- new_rlc->chain.first = rlci;
- new_rlc->chain.last = rlc->chain.last;
- rlc->chain.last = rlci->prev;
- ((LineartLineChainItem *)rlc->chain.last)->next = 0;
- rlci->prev = 0;
+ new_rlc->chain.first = eci;
+ new_rlc->chain.last = ec->chain.last;
+ ec->chain.last = eci->prev;
+ ((LineartEdgeChainItem *)ec->chain.last)->next = 0;
+ eci->prev = 0;
/* End the previous one. */
lineart_chain_append_point(rb,
- rlc,
- rlci->pos,
- rlci->gpos,
- rlci->normal,
- rlci->line_type,
+ ec,
+ eci->pos,
+ eci->gpos,
+ eci->normal,
+ eci->line_type,
fixed_occ,
fixed_mask,
- rlci->index);
- new_rlc->object_ref = rlc->object_ref;
- new_rlc->type = rlc->type;
- rlc = new_rlc;
- fixed_occ = rlci->occlusion;
- fixed_mask = rlci->transparency_mask;
- rlc->level = fixed_occ;
- rlc->transparency_mask = fixed_mask;
+ eci->index);
+ new_rlc->object_ref = ec->object_ref;
+ new_rlc->type = ec->type;
+ ec = new_rlc;
+ fixed_occ = eci->occlusion;
+ fixed_mask = eci->transparency_mask;
+ ec->level = fixed_occ;
+ ec->transparency_mask = fixed_mask;
}
}
}
- LISTBASE_FOREACH (LineartLineChain *, irlc, &rb->chains) {
- lineart_bounding_area_link_chain(rb, irlc);
+ LISTBASE_FOREACH (LineartEdgeChain *, iec, &rb->chains) {
+ lineart_bounding_area_link_chain(rb, iec);
}
}
@@ -629,12 +629,12 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
* Note: segment type (crease/material/contour...) is ambiguous after this.
*/
static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb),
- LineartLineChain *onto,
- LineartLineChain *sub,
+ LineartEdgeChain *onto,
+ LineartEdgeChain *sub,
int reverse_1,
int reverse_2)
{
- LineartLineChainItem *rlci;
+ LineartEdgeChainItem *eci;
if (onto->type == LRT_EDGE_FLAG_INTERSECTION) {
if (sub->object_ref) {
onto->object_ref = sub->object_ref;
@@ -650,38 +650,38 @@ static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb),
if (reverse_2) { /* L--R R-L. */
BLI_listbase_reverse(&sub->chain);
}
- rlci = sub->chain.first;
- if (lineart_point_overlapping(onto->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ eci = sub->chain.first;
+ if (lineart_point_overlapping(onto->chain.last, eci->pos[0], eci->pos[1], 1e-5)) {
BLI_pophead(&sub->chain);
if (sub->chain.first == NULL) {
return;
}
}
- ((LineartLineChainItem *)onto->chain.last)->next = sub->chain.first;
- ((LineartLineChainItem *)sub->chain.first)->prev = onto->chain.last;
+ ((LineartEdgeChainItem *)onto->chain.last)->next = sub->chain.first;
+ ((LineartEdgeChainItem *)sub->chain.first)->prev = onto->chain.last;
onto->chain.last = sub->chain.last;
}
else { /* L-R L--R. */
if (!reverse_2) { /* R-L L--R. */
BLI_listbase_reverse(&sub->chain);
}
- rlci = onto->chain.first;
- if (lineart_point_overlapping(sub->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ eci = onto->chain.first;
+ if (lineart_point_overlapping(sub->chain.last, eci->pos[0], eci->pos[1], 1e-5)) {
BLI_pophead(&onto->chain);
if (onto->chain.first == NULL) {
return;
}
}
- ((LineartLineChainItem *)sub->chain.last)->next = onto->chain.first;
- ((LineartLineChainItem *)onto->chain.first)->prev = sub->chain.last;
+ ((LineartEdgeChainItem *)sub->chain.last)->next = onto->chain.first;
+ ((LineartEdgeChainItem *)onto->chain.first)->prev = sub->chain.last;
onto->chain.first = sub->chain.first;
}
}
static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuffer *rb,
LineartBoundingArea *ba,
- LineartLineChain *rlc,
- LineartLineChainItem *rlci,
+ LineartEdgeChain *ec,
+ LineartEdgeChainItem *eci,
int occlusion,
unsigned char transparency_mask,
float dist,
@@ -694,12 +694,12 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
/* Keep using for loop because `cre` could be removed from the iteration before getting to the
* next one. */
LISTBASE_FOREACH_MUTABLE (LineartChainRegisterEntry *, cre, &ba->linked_chains) {
- if (cre->rlc->object_ref != rlc->object_ref) {
+ if (cre->ec->object_ref != ec->object_ref) {
if (!rb->fuzzy_everything) {
if (rb->fuzzy_intersections) {
/* If none of those are intersection lines... */
- if ((!(cre->rlc->type & LRT_EDGE_FLAG_INTERSECTION)) &&
- (!(rlc->type & LRT_EDGE_FLAG_INTERSECTION))) {
+ if ((!(cre->ec->type & LRT_EDGE_FLAG_INTERSECTION)) &&
+ (!(ec->type & LRT_EDGE_FLAG_INTERSECTION))) {
continue; /* We don't want to chain along different objects at the moment. */
}
}
@@ -708,18 +708,18 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
}
}
}
- if (cre->rlc->picked || cre->picked) {
+ if (cre->ec->picked || cre->picked) {
continue;
}
- if (cre->rlc == rlc || (!cre->rlc->chain.first) || (cre->rlc->level != occlusion) ||
- (cre->rlc->transparency_mask != transparency_mask)) {
+ if (cre->ec == ec || (!cre->ec->chain.first) || (cre->ec->level != occlusion) ||
+ (cre->ec->transparency_mask != transparency_mask)) {
continue;
}
if (!rb->fuzzy_everything) {
- if (cre->rlc->type != rlc->type) {
+ if (cre->ec->type != ec->type) {
if (rb->fuzzy_intersections) {
- if (!(cre->rlc->type == LRT_EDGE_FLAG_INTERSECTION ||
- rlc->type == LRT_EDGE_FLAG_INTERSECTION)) {
+ if (!(cre->ec->type == LRT_EDGE_FLAG_INTERSECTION ||
+ ec->type == LRT_EDGE_FLAG_INTERSECTION)) {
continue; /* Fuzzy intersections but no intersection line found. */
}
}
@@ -729,7 +729,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
}
}
- float new_len = len_v2v2(cre->rlci->pos, rlci->pos);
+ float new_len = len_v2v2(cre->eci->pos, eci->pos);
if (new_len < dist) {
closest_cre = cre;
dist = new_len;
@@ -748,7 +748,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
LISTBASE_FOREACH (LinkData *, ld, list) { \
LineartBoundingArea *sba = (LineartBoundingArea *)ld->data; \
adjacent_closest = lineart_chain_get_closest_cre( \
- rb, sba, rlc, rlci, occlusion, transparency_mask, dist, &adjacent_new_len, ba); \
+ rb, sba, ec, eci, occlusion, transparency_mask, dist, &adjacent_new_len, ba); \
if (adjacent_new_len < dist) { \
dist = adjacent_new_len; \
closest_cre = adjacent_closest; \
@@ -756,10 +756,10 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
} \
}
if (!caller_ba) {
- LRT_TEST_ADJACENT_AREAS(rlci->pos[0] - ba->l, &ba->lp);
- LRT_TEST_ADJACENT_AREAS(ba->r - rlci->pos[0], &ba->rp);
- LRT_TEST_ADJACENT_AREAS(ba->u - rlci->pos[1], &ba->up);
- LRT_TEST_ADJACENT_AREAS(rlci->pos[1] - ba->b, &ba->bp);
+ LRT_TEST_ADJACENT_AREAS(eci->pos[0] - ba->l, &ba->lp);
+ LRT_TEST_ADJACENT_AREAS(ba->r - eci->pos[0], &ba->rp);
+ LRT_TEST_ADJACENT_AREAS(ba->u - eci->pos[1], &ba->up);
+ LRT_TEST_ADJACENT_AREAS(eci->pos[1] - ba->b, &ba->bp);
}
if (result_new_len) {
(*result_new_len) = dist;
@@ -774,8 +774,8 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
*/
void MOD_lineart_chain_connect(LineartRenderBuffer *rb)
{
- LineartLineChain *rlc;
- LineartLineChainItem *rlci_l, *rlci_r;
+ LineartEdgeChain *ec;
+ LineartEdgeChainItem *rlci_l, *rlci_r;
LineartBoundingArea *ba_l, *ba_r;
LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre;
float dist = rb->chaining_image_threshold;
@@ -793,24 +793,24 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb)
rb->chains.last = rb->chains.first = NULL;
- while ((rlc = BLI_pophead(&swap)) != NULL) {
- rlc->next = rlc->prev = NULL;
- if (rlc->picked) {
+ while ((ec = BLI_pophead(&swap)) != NULL) {
+ ec->next = ec->prev = NULL;
+ if (ec->picked) {
continue;
}
- BLI_addtail(&rb->chains, rlc);
+ BLI_addtail(&rb->chains, ec);
- occlusion = rlc->level;
- transparency_mask = rlc->transparency_mask;
+ occlusion = ec->level;
+ transparency_mask = ec->transparency_mask;
- rlci_l = rlc->chain.first;
- rlci_r = rlc->chain.last;
+ rlci_l = ec->chain.first;
+ rlci_r = ec->chain.last;
while ((ba_l = lineart_bounding_area_get_end_point(rb, rlci_l)) &&
(ba_r = lineart_bounding_area_get_end_point(rb, rlci_r))) {
closest_cre_l = lineart_chain_get_closest_cre(
- rb, ba_l, rlc, rlci_l, occlusion, transparency_mask, dist, &dist_l, NULL);
+ rb, ba_l, ec, rlci_l, occlusion, transparency_mask, dist, &dist_l, NULL);
closest_cre_r = lineart_chain_get_closest_cre(
- rb, ba_r, rlc, rlci_r, occlusion, transparency_mask, dist, &dist_r, NULL);
+ rb, ba_r, ec, rlci_r, occlusion, transparency_mask, dist, &dist_r, NULL);
if (closest_cre_l && closest_cre_r) {
if (dist_l < dist_r) {
closest_cre = closest_cre_l;
@@ -834,56 +834,56 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb)
break;
}
closest_cre->picked = 1;
- closest_cre->rlc->picked = 1;
+ closest_cre->ec->picked = 1;
if (closest_cre->is_left) {
- lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 0);
+ lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 0);
}
else {
- lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 1);
+ lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 1);
}
- BLI_remlink(&swap, closest_cre->rlc);
- rlci_l = rlc->chain.first;
- rlci_r = rlc->chain.last;
+ BLI_remlink(&swap, closest_cre->ec);
+ rlci_l = ec->chain.first;
+ rlci_r = ec->chain.last;
}
- rlc->picked = 1;
+ ec->picked = 1;
}
}
/**
* Length is in image space.
*/
-float MOD_lineart_chain_compute_length(LineartLineChain *rlc)
+float MOD_lineart_chain_compute_length(LineartEdgeChain *ec)
{
- LineartLineChainItem *rlci;
+ LineartEdgeChainItem *eci;
float offset_accum = 0;
float dist;
float last_point[2];
- rlci = rlc->chain.first;
- copy_v2_v2(last_point, rlci->pos);
- for (rlci = rlc->chain.first; rlci; rlci = rlci->next) {
- dist = len_v2v2(rlci->pos, last_point);
+ eci = ec->chain.first;
+ copy_v2_v2(last_point, eci->pos);
+ for (eci = ec->chain.first; eci; eci = eci->next) {
+ dist = len_v2v2(eci->pos, last_point);
offset_accum += dist;
- copy_v2_v2(last_point, rlci->pos);
+ copy_v2_v2(last_point, eci->pos);
}
return offset_accum;
}
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold)
{
- LineartLineChain *rlc, *next_rlc;
- for (rlc = rb->chains.first; rlc; rlc = next_rlc) {
- next_rlc = rlc->next;
- if (MOD_lineart_chain_compute_length(rlc) < threshold) {
- BLI_remlink(&rb->chains, rlc);
+ LineartEdgeChain *ec, *next_rlc;
+ for (ec = rb->chains.first; ec; ec = next_rlc) {
+ next_rlc = ec->next;
+ if (MOD_lineart_chain_compute_length(ec) < threshold) {
+ BLI_remlink(&rb->chains, ec);
}
}
}
-int MOD_lineart_chain_count(const LineartLineChain *rlc)
+int MOD_lineart_chain_count(const LineartEdgeChain *ec)
{
int count = 0;
- LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) {
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
count++;
}
return count;
@@ -894,8 +894,8 @@ void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb)
if (rb == NULL) {
return;
}
- LISTBASE_FOREACH (LineartLineChain *, rlc, &rb->chains) {
- rlc->picked = 0;
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
+ ec->picked = 0;
}
}
@@ -905,8 +905,8 @@ void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb)
*/
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad)
{
- LineartLineChain *rlc, *new_rlc;
- LineartLineChainItem *rlci, *next_rlci, *prev_rlci;
+ LineartEdgeChain *ec, *new_rlc;
+ LineartEdgeChainItem *eci, *next_rlci, *prev_rlci;
ListBase swap = {0};
swap.first = rb->chains.first;
@@ -914,43 +914,43 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol
rb->chains.last = rb->chains.first = NULL;
- while ((rlc = BLI_pophead(&swap)) != NULL) {
- rlc->next = rlc->prev = NULL;
- BLI_addtail(&rb->chains, rlc);
- LineartLineChainItem *first_rlci = (LineartLineChainItem *)rlc->chain.first;
- for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
- next_rlci = rlci->next;
- prev_rlci = rlci->prev;
+ while ((ec = BLI_pophead(&swap)) != NULL) {
+ ec->next = ec->prev = NULL;
+ BLI_addtail(&rb->chains, ec);
+ LineartEdgeChainItem *first_rlci = (LineartEdgeChainItem *)ec->chain.first;
+ for (eci = first_rlci->next; eci; eci = next_rlci) {
+ next_rlci = eci->next;
+ prev_rlci = eci->prev;
float angle = M_PI;
if (next_rlci && prev_rlci) {
- angle = angle_v2v2v2(prev_rlci->pos, rlci->pos, next_rlci->pos);
+ angle = angle_v2v2v2(prev_rlci->pos, eci->pos, next_rlci->pos);
}
else {
break; /* No need to split at the last point anyway.*/
}
if (angle < angle_threshold_rad) {
new_rlc = lineart_chain_create(rb);
- new_rlc->chain.first = rlci;
- new_rlc->chain.last = rlc->chain.last;
- rlc->chain.last = rlci->prev;
- ((LineartLineChainItem *)rlc->chain.last)->next = 0;
- rlci->prev = 0;
+ new_rlc->chain.first = eci;
+ new_rlc->chain.last = ec->chain.last;
+ ec->chain.last = eci->prev;
+ ((LineartEdgeChainItem *)ec->chain.last)->next = 0;
+ eci->prev = 0;
/* End the previous one. */
lineart_chain_append_point(rb,
- rlc,
- rlci->pos,
- rlci->gpos,
- rlci->normal,
- rlci->line_type,
- rlc->level,
- rlci->transparency_mask,
- rlci->index);
- new_rlc->object_ref = rlc->object_ref;
- new_rlc->type = rlc->type;
- new_rlc->level = rlc->level;
- new_rlc->transparency_mask = rlc->transparency_mask;
- rlc = new_rlc;
+ ec,
+ eci->pos,
+ eci->gpos,
+ eci->normal,
+ eci->line_type,
+ ec->level,
+ eci->transparency_mask,
+ eci->index);
+ new_rlc->object_ref = ec->object_ref;
+ new_rlc->type = ec->type;
+ new_rlc->level = ec->level;
+ new_rlc->transparency_mask = ec->transparency_mask;
+ ec = new_rlc;
}
}
}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 7fb3981d9dc..06d63c3ddab 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -29,6 +29,8 @@
#include "BLI_task.h"
#include "BLI_utildefines.h"
+#include "PIL_time.h"
+
#include "BKE_camera.h"
#include "BKE_collection.h"
#include "BKE_customdata.h"
@@ -52,8 +54,6 @@
#include "DNA_scene_types.h"
#include "MEM_guardedalloc.h"
-#include "BLI_math.h"
-
#include "bmesh.h"
#include "bmesh_class.h"
#include "bmesh_tools.h"
@@ -63,7 +63,7 @@
static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb,
LineartEdge *e);
-static void lineart_bounding_area_link_line(LineartRenderBuffer *rb,
+static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb,
LineartBoundingArea *root_ba,
LineartEdge *e);
@@ -86,14 +86,14 @@ static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb,
static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
LineartBoundingArea *root_ba,
- LineartTriangle *rt,
+ LineartTriangle *tri,
double *LRUB,
int recursive,
int recursive_level,
bool do_intersection);
static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl,
- const LineartTriangle *rt,
+ const LineartTriangle *tri,
const LineartEdge *e,
const double *override_camera_loc,
const bool override_cam_is_persp,
@@ -107,35 +107,35 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl,
static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e);
-static void lineart_discard_segment(LineartRenderBuffer *rb, LineartLineSegment *rls)
+static void lineart_discard_segment(LineartRenderBuffer *rb, LineartEdgeSegment *es)
{
BLI_spin_lock(&rb->lock_cuts);
- memset(rls, 0, sizeof(LineartLineSegment));
+ memset(es, 0, sizeof(LineartEdgeSegment));
/* Storing the node for potentially reuse the memory for new segment data.
* Line Art data is not freed after all calculations are done. */
- BLI_addtail(&rb->wasted_cuts, rls);
+ BLI_addtail(&rb->wasted_cuts, es);
BLI_spin_unlock(&rb->lock_cuts);
}
-static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb)
+static LineartEdgeSegment *lineart_give_segment(LineartRenderBuffer *rb)
{
BLI_spin_lock(&rb->lock_cuts);
/* See if there is any already allocated memory we can reuse. */
if (rb->wasted_cuts.first) {
- LineartLineSegment *rls = (LineartLineSegment *)BLI_pophead(&rb->wasted_cuts);
+ LineartEdgeSegment *es = (LineartEdgeSegment *)BLI_pophead(&rb->wasted_cuts);
BLI_spin_unlock(&rb->lock_cuts);
- memset(rls, 0, sizeof(LineartLineSegment));
- return rls;
+ memset(es, 0, sizeof(LineartEdgeSegment));
+ return es;
}
BLI_spin_unlock(&rb->lock_cuts);
/* Otherwise allocate some new memory. */
- return (LineartLineSegment *)lineart_mem_aquire_thread(&rb->render_data_pool,
- sizeof(LineartLineSegment));
+ return (LineartEdgeSegment *)lineart_mem_acquire_thread(&rb->render_data_pool,
+ sizeof(LineartEdgeSegment));
}
/**
@@ -144,9 +144,9 @@ static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb)
static void lineart_edge_cut(
LineartRenderBuffer *rb, LineartEdge *e, double start, double end, uchar transparency_mask)
{
- LineartLineSegment *rls, *irls, *next_rls, *prev_rls;
- LineartLineSegment *cut_start_before = 0, *cut_end_before = 0;
- LineartLineSegment *ns = 0, *ns2 = 0;
+ LineartEdgeSegment *es, *ies, *next_es, *prev_es;
+ LineartEdgeSegment *cut_start_before = 0, *cut_end_before = 0;
+ LineartEdgeSegment *ns = 0, *ns2 = 0;
int untouched = 0;
/* If for some reason the occlusion function may give a result that has zero length, or reversed
@@ -173,18 +173,18 @@ static void lineart_edge_cut(
/* Begin looking for starting position of the segment. */
/* Not using a list iteration macro because of it more clear when using for loops to iterate
* through the segments. */
- for (rls = e->segments.first; rls; rls = rls->next) {
- if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, start)) {
- cut_start_before = rls;
+ for (es = e->segments.first; es; es = es->next) {
+ if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, start)) {
+ cut_start_before = es;
ns = cut_start_before;
break;
}
- if (rls->next == NULL) {
+ if (es->next == NULL) {
break;
}
- irls = rls->next;
- if (irls->at > start + 1e-09 && start > rls->at) {
- cut_start_before = irls;
+ ies = es->next;
+ if (ies->at > start + 1e-09 && start > es->at) {
+ cut_start_before = ies;
ns = lineart_give_segment(rb);
break;
}
@@ -192,25 +192,25 @@ static void lineart_edge_cut(
if (!cut_start_before && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
untouched = 1;
}
- for (rls = cut_start_before; rls; rls = rls->next) {
+ for (es = cut_start_before; es; es = es->next) {
/* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle
* strip). */
- if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, end)) {
- cut_end_before = rls;
+ if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, end)) {
+ cut_end_before = es;
ns2 = cut_end_before;
break;
}
- /* This check is to prevent `rls->at == 1.0` (where we don't need to cut because we are at the
+ /* This check is to prevent `es->at == 1.0` (where we don't need to cut because we are at the
* end point). */
- if (!rls->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
- cut_end_before = rls;
+ if (!es->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ cut_end_before = es;
ns2 = cut_end_before;
untouched = 1;
break;
}
/* When an actual cut is needed in the line. */
- if (rls->at > end) {
- cut_end_before = rls;
+ if (es->at > end) {
+ cut_end_before = es;
ns2 = lineart_give_segment(rb);
break;
}
@@ -233,9 +233,9 @@ static void lineart_edge_cut(
if (cut_start_before) {
if (cut_start_before != ns) {
/* Insert cutting points for when a new cut is needed. */
- irls = cut_start_before->prev ? cut_start_before->prev : NULL;
- ns->occlusion = irls ? irls->occlusion : 0;
- ns->transparency_mask = irls->transparency_mask;
+ ies = cut_start_before->prev ? cut_start_before->prev : NULL;
+ ns->occlusion = ies ? ies->occlusion : 0;
+ ns->transparency_mask = ies->transparency_mask;
BLI_insertlinkbefore(&e->segments, cut_start_before, ns);
}
/* Otherwise we already found a existing cutting point, no need to insert a new one. */
@@ -243,24 +243,24 @@ static void lineart_edge_cut(
else {
/* We have yet to reach a existing cutting point even after we searched the whole line, so we
* append the new cut to the end. */
- irls = e->segments.last;
- ns->occlusion = irls->occlusion;
- ns->transparency_mask = irls->transparency_mask;
+ ies = e->segments.last;
+ ns->occlusion = ies->occlusion;
+ ns->transparency_mask = ies->transparency_mask;
BLI_addtail(&e->segments, ns);
}
if (cut_end_before) {
/* The same manipulation as on "cut_start_before". */
if (cut_end_before != ns2) {
- irls = cut_end_before->prev ? cut_end_before->prev : NULL;
- ns2->occlusion = irls ? irls->occlusion : 0;
- ns2->transparency_mask = irls ? irls->transparency_mask : 0;
+ ies = cut_end_before->prev ? cut_end_before->prev : NULL;
+ ns2->occlusion = ies ? ies->occlusion : 0;
+ ns2->transparency_mask = ies ? ies->transparency_mask : 0;
BLI_insertlinkbefore(&e->segments, cut_end_before, ns2);
}
}
else {
- irls = e->segments.last;
- ns2->occlusion = irls->occlusion;
- ns2->transparency_mask = irls->transparency_mask;
+ ies = e->segments.last;
+ ns2->occlusion = ies->occlusion;
+ ns2->transparency_mask = ies->transparency_mask;
BLI_addtail(&e->segments, ns2);
}
@@ -276,29 +276,29 @@ static void lineart_edge_cut(
}
/* Register 1 level of occlusion for all touched segments. */
- for (rls = ns; rls && rls != ns2; rls = rls->next) {
- rls->occlusion++;
- rls->transparency_mask |= transparency_mask;
+ for (es = ns; es && es != ns2; es = es->next) {
+ es->occlusion++;
+ es->transparency_mask |= transparency_mask;
}
/* Reduce adjacent cutting points of the same level, which saves memory. */
char min_occ = 127;
- prev_rls = NULL;
- for (rls = e->segments.first; rls; rls = next_rls) {
- next_rls = rls->next;
+ prev_es = NULL;
+ for (es = e->segments.first; es; es = next_es) {
+ next_es = es->next;
- if (prev_rls && prev_rls->occlusion == rls->occlusion &&
- prev_rls->transparency_mask == rls->transparency_mask) {
- BLI_remlink(&e->segments, rls);
+ if (prev_es && prev_es->occlusion == es->occlusion &&
+ prev_es->transparency_mask == es->transparency_mask) {
+ BLI_remlink(&e->segments, es);
/* This puts the node back to the render buffer, if more cut happens, these unused nodes get
* picked first. */
- lineart_discard_segment(rb, rls);
+ lineart_discard_segment(rb, es);
continue;
}
- min_occ = MIN2(min_occ, rls->occlusion);
+ min_occ = MIN2(min_occ, es->occlusion);
- prev_rls = rls;
+ prev_es = es;
}
e->min_occ = min_occ;
}
@@ -306,12 +306,42 @@ static void lineart_edge_cut(
/**
* To see if given line is connected to an adjacent intersection line.
*/
-BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *rt)
+BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *tri)
{
LineartVertIntersection *v1 = (void *)e->v1;
LineartVertIntersection *v2 = (void *)e->v2;
- return ((v1->base.flag && v1->intersecting_with == rt) ||
- (v2->base.flag && v2->intersecting_with == rt));
+ return ((v1->base.flag && v1->intersecting_with == tri) ||
+ (v2->base.flag && v2->intersecting_with == tri));
+}
+
+static void lineart_bounding_area_triangle_add(LineartRenderBuffer *rb,
+ LineartBoundingArea *ba,
+ LineartTriangle *tri)
+{
+ if (ba->triangle_count >= ba->max_triangle_count) {
+ LineartTriangle **new_array = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count * 2);
+ memcpy(new_array, ba->linked_triangles, sizeof(LineartTriangle *) * ba->max_triangle_count);
+ ba->max_triangle_count *= 2;
+ ba->linked_triangles = new_array;
+ }
+ ba->linked_triangles[ba->triangle_count] = tri;
+ ba->triangle_count++;
+}
+
+static void lineart_bounding_area_line_add(LineartRenderBuffer *rb,
+ LineartBoundingArea *ba,
+ LineartEdge *e)
+{
+ if (ba->line_count >= ba->max_line_count) {
+ LineartEdge **new_array = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartEdge *) * ba->max_line_count * 2);
+ memcpy(new_array, ba->linked_lines, sizeof(LineartEdge *) * ba->max_line_count);
+ ba->max_line_count *= 2;
+ ba->linked_lines = new_array;
+ }
+ ba->linked_lines[ba->line_count] = e;
+ ba->line_count++;
}
static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *e, int thread_id)
@@ -319,7 +349,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *
double x = e->v1->fbcoord[0], y = e->v1->fbcoord[1];
LineartBoundingArea *ba = lineart_edge_first_bounding_area(rb, e);
LineartBoundingArea *nba = ba;
- LineartTriangleThread *rt;
+ LineartTriangleThread *tri;
/* These values are used for marching along the line. */
double l, r;
@@ -334,16 +364,16 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *
while (nba) {
- LISTBASE_FOREACH (LinkData *, lip, &nba->linked_triangles) {
- rt = lip->data;
+ for (int i = 0; i < nba->triangle_count; i++) {
+ tri = (LineartTriangleThread *)nba->linked_triangles[i];
/* If we are already testing the line in this thread, then don't do it. */
- if (rt->testing_e[thread_id] == e || (rt->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) ||
- lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)rt)) {
+ if (tri->testing_e[thread_id] == e || (tri->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) ||
+ lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)tri)) {
continue;
}
- rt->testing_e[thread_id] = e;
+ tri->testing_e[thread_id] = e;
if (lineart_triangle_edge_image_space_occlusion(&rb->lock_task,
- (const LineartTriangle *)rt,
+ (const LineartTriangle *)tri,
e,
rb->camera_pos,
rb->cam_is_persp,
@@ -354,7 +384,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *
rb->shift_y,
&l,
&r)) {
- lineart_edge_cut(rb, e, l, r, rt->base.transparency_mask);
+ lineart_edge_cut(rb, e, l, r, tri->base.transparency_mask);
if (e->min_occ > rb->max_occlusion_level) {
/* No need to calculate any longer on this line because no level more than set value is
* going to show up in the rendered result. */
@@ -376,18 +406,18 @@ static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRend
BLI_spin_lock(&rb->lock_task);
#define LRT_ASSIGN_OCCLUSION_TASK(name) \
- if (rb->name##_managed) { \
- data = rb->name##_managed; \
- rti->name = (void *)data; \
+ if (rb->name.last) { \
+ data = rb->name.last; \
+ rti->name.first = (void *)data; \
for (i = 0; i < LRT_THREAD_EDGE_COUNT && data; i++) { \
data = data->next; \
} \
- rti->name##_end = data; \
- rb->name##_managed = data; \
+ rti->name.last = data; \
+ rb->name.last = data; \
res = 1; \
} \
else { \
- rti->name = NULL; \
+ rti->name.first = rti->name.last = NULL; \
}
LRT_ASSIGN_OCCLUSION_TASK(contour);
@@ -410,23 +440,23 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR
while (lineart_occlusion_make_task_info(rb, rti)) {
- for (eip = rti->contour; eip && eip != rti->contour_end; eip = eip->next) {
+ for (eip = rti->contour.first; eip && eip != rti->contour.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
- for (eip = rti->crease; eip && eip != rti->crease_end; eip = eip->next) {
+ for (eip = rti->crease.first; eip && eip != rti->crease.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
- for (eip = rti->intersection; eip && eip != rti->intersection_end; eip = eip->next) {
+ for (eip = rti->intersection.first; eip && eip != rti->intersection.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
- for (eip = rti->material; eip && eip != rti->material_end; eip = eip->next) {
+ for (eip = rti->material.first; eip && eip != rti->material.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
- for (eip = rti->edge_mark; eip && eip != rti->edge_mark_end; eip = eip->next) {
+ for (eip = rti->edge_mark.first; eip && eip != rti->edge_mark.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
}
@@ -444,13 +474,15 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb)
"Task Pool");
int i;
- rb->contour_managed = rb->contours;
- rb->crease_managed = rb->crease_lines;
- rb->intersection_managed = rb->intersection_lines;
- rb->material_managed = rb->material_lines;
- rb->edge_mark_managed = rb->edge_marks;
+ /* The "last" entry is used to store worker progress in the whole list.
+ * These list themselves are single-direction linked, with list.first being the head. */
+ rb->contour.last = rb->contour.first;
+ rb->crease.last = rb->crease.first;
+ rb->intersection.last = rb->intersection.first;
+ rb->material.last = rb->material.first;
+ rb->edge_mark.last = rb->edge_mark.first;
- TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH);
+ TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
for (i = 0; i < thread_count; i++) {
rti[i].thread_id = i;
@@ -627,75 +659,75 @@ static bool lineart_point_inside_triangle3d(double v[3], double v0[3], double v1
*/
static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBuffer *rb)
{
- LineartElementLinkNode *reln;
+ LineartElementLinkNode *eln;
/* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles
* are relatively small. */
- LineartTriangle *render_triangles = lineart_mem_aquire(&rb->render_data_pool,
- 64 * rb->triangle_size);
+ LineartTriangle *render_triangles = lineart_mem_acquire(&rb->render_data_pool,
+ 64 * rb->triangle_size);
- reln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers,
- &rb->render_data_pool,
- render_triangles,
- sizeof(LineartElementLinkNode));
- reln->element_count = 64;
- reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+ eln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers,
+ &rb->render_data_pool,
+ render_triangles,
+ sizeof(LineartElementLinkNode));
+ eln->element_count = 64;
+ eln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
- return reln;
+ return eln;
}
static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer *rb)
{
- LineartElementLinkNode *reln;
+ LineartElementLinkNode *eln;
- LineartVert *render_vertices = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartVert) * 64);
+ LineartVert *render_vertices = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartVert) * 64);
- reln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers,
- &rb->render_data_pool,
- render_vertices,
- sizeof(LineartElementLinkNode));
- reln->element_count = 64;
- reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+ eln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers,
+ &rb->render_data_pool,
+ render_vertices,
+ sizeof(LineartElementLinkNode));
+ eln->element_count = 64;
+ eln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
- return reln;
+ return eln;
}
static LineartElementLinkNode *lineart_memory_get_edge_space(LineartRenderBuffer *rb)
{
- LineartElementLinkNode *reln;
+ LineartElementLinkNode *eln;
- LineartEdge *render_edges = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge) * 64);
+ LineartEdge *render_edges = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * 64);
- reln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers,
- &rb->render_data_pool,
- render_edges,
- sizeof(LineartElementLinkNode));
- reln->element_count = 64;
- reln->crease_threshold = rb->crease_threshold;
- reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+ eln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers,
+ &rb->render_data_pool,
+ render_edges,
+ sizeof(LineartElementLinkNode));
+ eln->element_count = 64;
+ eln->crease_threshold = rb->crease_threshold;
+ eln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
- return reln;
+ return eln;
}
-static void lineart_triangle_post(LineartTriangle *rt, LineartTriangle *orig)
+static void lineart_triangle_post(LineartTriangle *tri, LineartTriangle *orig)
{
/* Just re-assign normal and set cull flag. */
- copy_v3_v3_db(rt->gn, orig->gn);
- rt->flags = LRT_CULL_GENERATED;
+ copy_v3_v3_db(tri->gn, orig->gn);
+ tri->flags = LRT_CULL_GENERATED;
}
-static void lineart_triangle_set_cull_flag(LineartTriangle *rt, uchar flag)
+static void lineart_triangle_set_cull_flag(LineartTriangle *tri, uchar flag)
{
- uchar intersection_only = (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY);
- rt->flags = flag;
- rt->flags |= intersection_only;
+ uchar intersection_only = (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY);
+ tri->flags = flag;
+ tri->flags |= intersection_only;
}
-static bool lineart_edge_match(LineartTriangle *rt, LineartEdge *e, int v1, int v2)
+static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int v2)
{
- return ((rt->v[v1] == e->v1 && rt->v[v2] == e->v2) ||
- (rt->v[v2] == e->v1 && rt->v[v1] == e->v2));
+ return ((tri->v[v1] == e->v1 && tri->v[v2] == e->v2) ||
+ (tri->v[v2] == e->v1 && tri->v[v1] == e->v2));
}
/**
@@ -703,7 +735,7 @@ static bool lineart_edge_match(LineartTriangle *rt, LineartEdge *e, int v1, int
* reversed by the caller so don't need to implement one in a different direction.
*/
static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
int in0,
int in1,
int in2,
@@ -728,67 +760,67 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
char new_flag = 0;
LineartEdge *new_e, *e, *old_e;
- LineartLineSegment *rls;
- LineartTriangleAdjacent *rta;
+ LineartEdgeSegment *es;
+ LineartTriangleAdjacent *ta;
- if (rt->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) {
+ if (tri->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) {
return;
}
- /* See definition of rt->intersecting_verts and the usage in
+ /* See definition of tri->intersecting_verts and the usage in
* lineart_geometry_object_load() for details. */
- rta = (void *)rt->intersecting_verts;
+ ta = (void *)tri->intersecting_verts;
- LineartVert *rv = &((LineartVert *)v_eln->pointer)[v_count];
- LineartTriangle *rt1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count);
- LineartTriangle *rt2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1));
+ LineartVert *vt = &((LineartVert *)v_eln->pointer)[v_count];
+ LineartTriangle *tri1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count);
+ LineartTriangle *tri2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1));
new_e = &((LineartEdge *)e_eln->pointer)[e_count];
- /* Init `rl` to the last `rl` entry. */
+ /* Init `edge` to the last `edge` entry. */
e = new_e;
-#define INCREASE_RL \
- e_count++; \
+#define INCREASE_EDGE \
v1_obi = e->v1_obindex; \
v2_obi = e->v2_obindex; \
new_e = &((LineartEdge *)e_eln->pointer)[e_count]; \
+ e_count++; \
e = new_e; \
e->v1_obindex = v1_obi; \
e->v2_obindex = v2_obi; \
- rls = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineSegment)); \
- BLI_addtail(&e->segments, rls);
+ es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); \
+ BLI_addtail(&e->segments, es);
-#define SELECT_RL(e_num, v1_link, v2_link, newrt) \
- if (rta->e[e_num]) { \
- old_e = rta->e[e_num]; \
+#define SELECT_EDGE(e_num, v1_link, v2_link, new_tri) \
+ if (ta->e[e_num]) { \
+ old_e = ta->e[e_num]; \
new_flag = old_e->flags; \
old_e->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
- INCREASE_RL \
+ INCREASE_EDGE \
e->v1 = (v1_link); \
e->v2 = (v2_link); \
e->flags = new_flag; \
e->object_ref = ob; \
- e->t1 = ((old_e->t1 == rt) ? (newrt) : (old_e->t1)); \
- e->t2 = ((old_e->t2 == rt) ? (newrt) : (old_e->t2)); \
+ e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \
+ e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \
lineart_add_edge_to_list(rb, e); \
}
-#define RELINK_RL(e_num, newrt) \
- if (rta->e[e_num]) { \
- old_e = rta->e[e_num]; \
- old_e->t1 = ((old_e->t1 == rt) ? (newrt) : (old_e->t1)); \
- old_e->t2 = ((old_e->t2 == rt) ? (newrt) : (old_e->t2)); \
+#define RELINK_EDGE(e_num, new_tri) \
+ if (ta->e[e_num]) { \
+ old_e = ta->e[e_num]; \
+ old_e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \
+ old_e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \
}
-#define REMOVE_TRIANGLE_RL \
- if (rta->e[0]) { \
- rta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+#define REMOVE_TRIANGLE_EDGE \
+ if (ta->e[0]) { \
+ ta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
} \
- if (rta->e[1]) { \
- rta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+ if (ta->e[1]) { \
+ ta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
} \
- if (rta->e[2]) { \
- rta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+ if (ta->e[2]) { \
+ ta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
}
switch (in0 + in1 + in2) {
@@ -797,13 +829,13 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
case 3:
/* Triangle completely behind near plane, throw it away
* also remove render lines form being computed. */
- lineart_triangle_set_cull_flag(rt, LRT_CULL_DISCARD);
- REMOVE_TRIANGLE_RL
+ lineart_triangle_set_cull_flag(tri, LRT_CULL_DISCARD);
+ REMOVE_TRIANGLE_EDGE
return;
case 2:
/* Two points behind near plane, cut those and
* generate 2 new points, 3 lines and 1 triangle. */
- lineart_triangle_set_cull_flag(rt, LRT_CULL_USED);
+ lineart_triangle_set_cull_flag(tri, LRT_CULL_USED);
/**
* (!in0) means "when point 0 is visible".
@@ -828,136 +860,136 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
if (!in0) {
/* Cut point for line 2---|-----0. */
- sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
/* Assign it to a new point. */
- interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[2]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[2]->index;
/* Cut point for line 1---|-----0. */
- sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
/* Assign it to another new point. */
- interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[1]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[1]->index;
/* New line connecting two new points. */
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
/* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as
- * `rt->rl` and `rt->v` has the same sequence. and the winding direction
+ * `tri->edge` and `tri->v` has the same sequence. and the winding direction
* can be either CW or CCW but needs to be consistent throughout the calculation. */
- e->v1 = &rv[1];
- e->v2 = &rv[0];
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
/* Only one adjacent triangle, because the other side is the near plane. */
/* Use `tl` or `tr` doesn't matter. */
- e->t1 = rt1;
+ e->t1 = tri1;
e->object_ref = ob;
/* New line connecting original point 0 and a new point, only when it's a selected line. */
- SELECT_RL(2, rt->v[0], &rv[0], rt1)
+ SELECT_EDGE(2, tri->v[0], &vt[0], tri1)
/* New line connecting original point 0 and another new point. */
- SELECT_RL(0, rt->v[0], &rv[1], rt1)
+ SELECT_EDGE(0, tri->v[0], &vt[1], tri1)
/* Re-assign triangle point array to two new points. */
- rt1->v[0] = rt->v[0];
- rt1->v[1] = &rv[1];
- rt1->v[2] = &rv[0];
+ tri1->v[0] = tri->v[0];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = &vt[0];
- lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(tri1, tri);
v_count += 2;
t_count += 1;
}
else if (!in2) {
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[0]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[0]->index;
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[1]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[1]->index;
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[0];
- e->v2 = &rv[1];
- e->t1 = rt1;
+ e->v1 = &vt[0];
+ e->v2 = &vt[1];
+ e->t1 = tri1;
e->object_ref = ob;
- SELECT_RL(2, rt->v[2], &rv[0], rt1)
- SELECT_RL(1, rt->v[2], &rv[1], rt1)
+ SELECT_EDGE(2, tri->v[2], &vt[0], tri1)
+ SELECT_EDGE(1, tri->v[2], &vt[1], tri1)
- rt1->v[0] = &rv[0];
- rt1->v[1] = &rv[1];
- rt1->v[2] = rt->v[2];
+ tri1->v[0] = &vt[0];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = tri->v[2];
- lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(tri1, tri);
v_count += 2;
t_count += 1;
}
else if (!in1) {
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[2]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[2]->index;
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[0]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[0]->index;
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[1];
- e->v2 = &rv[0];
- e->t1 = rt1;
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
+ e->t1 = tri1;
e->object_ref = ob;
- SELECT_RL(1, rt->v[1], &rv[0], rt1)
- SELECT_RL(0, rt->v[1], &rv[1], rt1)
+ SELECT_EDGE(1, tri->v[1], &vt[0], tri1)
+ SELECT_EDGE(0, tri->v[1], &vt[1], tri1)
- rt1->v[0] = &rv[0];
- rt1->v[1] = rt->v[1];
- rt1->v[2] = &rv[1];
+ tri1->v[0] = &vt[0];
+ tri1->v[1] = tri->v[1];
+ tri1->v[2] = &vt[1];
- lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(tri1, tri);
v_count += 2;
t_count += 1;
@@ -966,7 +998,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
case 1:
/* One point behind near plane, cut those and
* generate 2 new points, 4 lines and 2 triangles. */
- lineart_triangle_set_cull_flag(rt, LRT_CULL_USED);
+ lineart_triangle_set_cull_flag(tri, LRT_CULL_USED);
/**
* (in0) means "when point 0 is invisible".
@@ -993,152 +1025,152 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
*/
if (in0) {
/* Cut point for line 0---|------1. */
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot2 / (dot1 + dot2);
/* Assign to a new point. */
- interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[0]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[0]->index;
/* Cut point for line 0---|------2. */
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot2 / (dot1 + dot2);
/* Assign to other new point. */
- interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[0]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[0]->index;
/* New line connects two new points. */
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[1];
- e->v2 = &rv[0];
- e->t1 = rt1;
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
+ e->t1 = tri1;
e->object_ref = ob;
/* New line connects new point 0 and old point 1,
* this is a border line. */
- SELECT_RL(0, rt->v[1], &rv[0], rt1)
- SELECT_RL(2, rt->v[2], &rv[1], rt2)
- RELINK_RL(1, rt2)
+ SELECT_EDGE(0, tri->v[1], &vt[0], tri1)
+ SELECT_EDGE(2, tri->v[2], &vt[1], tri2)
+ RELINK_EDGE(1, tri2)
/* We now have one triangle closed. */
- rt1->v[0] = rt->v[1];
- rt1->v[1] = &rv[1];
- rt1->v[2] = &rv[0];
+ tri1->v[0] = tri->v[1];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = &vt[0];
/* Close the second triangle. */
- rt2->v[0] = &rv[1];
- rt2->v[1] = rt->v[1];
- rt2->v[2] = rt->v[2];
+ tri2->v[0] = &vt[1];
+ tri2->v[1] = tri->v[1];
+ tri2->v[2] = tri->v[2];
- lineart_triangle_post(rt1, rt);
- lineart_triangle_post(rt2, rt);
+ lineart_triangle_post(tri1, tri);
+ lineart_triangle_post(tri2, tri);
v_count += 2;
t_count += 2;
}
else if (in1) {
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[1]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[1]->index;
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[1]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[1]->index;
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[1];
- e->v2 = &rv[0];
- e->t1 = rt1;
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
+ e->t1 = tri1;
e->object_ref = ob;
- SELECT_RL(1, rt->v[2], &rv[0], rt1)
- SELECT_RL(0, rt->v[0], &rv[1], rt2)
- RELINK_RL(2, rt2)
+ SELECT_EDGE(1, tri->v[2], &vt[0], tri1)
+ SELECT_EDGE(0, tri->v[0], &vt[1], tri2)
+ RELINK_EDGE(2, tri2)
- rt1->v[0] = rt->v[2];
- rt1->v[1] = &rv[1];
- rt1->v[2] = &rv[0];
+ tri1->v[0] = tri->v[2];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = &vt[0];
- rt2->v[0] = &rv[1];
- rt2->v[1] = rt->v[2];
- rt2->v[2] = rt->v[0];
+ tri2->v[0] = &vt[1];
+ tri2->v[1] = tri->v[2];
+ tri2->v[2] = tri->v[0];
- lineart_triangle_post(rt1, rt);
- lineart_triangle_post(rt2, rt);
+ lineart_triangle_post(tri1, tri);
+ lineart_triangle_post(tri2, tri);
v_count += 2;
t_count += 2;
}
else if (in2) {
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[2]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[2]->index;
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[2]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[2]->index;
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[1];
- e->v2 = &rv[0];
- e->t1 = rt1;
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
+ e->t1 = tri1;
e->object_ref = ob;
- SELECT_RL(2, rt->v[0], &rv[0], rt1)
- SELECT_RL(1, rt->v[1], &rv[1], rt2)
- RELINK_RL(0, rt2)
+ SELECT_EDGE(2, tri->v[0], &vt[0], tri1)
+ SELECT_EDGE(1, tri->v[1], &vt[1], tri2)
+ RELINK_EDGE(0, tri2)
- rt1->v[0] = rt->v[0];
- rt1->v[1] = &rv[1];
- rt1->v[2] = &rv[0];
+ tri1->v[0] = tri->v[0];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = &vt[0];
- rt2->v[0] = &rv[1];
- rt2->v[1] = rt->v[0];
- rt2->v[2] = rt->v[1];
+ tri2->v[0] = &vt[1];
+ tri2->v[1] = tri->v[0];
+ tri2->v[2] = tri->v[1];
- lineart_triangle_post(rt1, rt);
- lineart_triangle_post(rt2, rt);
+ lineart_triangle_post(tri1, tri);
+ lineart_triangle_post(tri2, tri);
v_count += 2;
t_count += 2;
@@ -1149,10 +1181,10 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
*r_e_count = e_count;
*r_t_count = t_count;
-#undef INCREASE_RL
-#undef SELECT_RL
-#undef RELINK_RL
-#undef REMOVE_TRIANGLE_RL
+#undef INCREASE_EDGE
+#undef SELECT_EDGE
+#undef RELINK_EDGE
+#undef REMOVE_TRIANGLE_EDGE
}
/**
@@ -1163,7 +1195,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
*/
static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
{
- LineartTriangle *rt;
+ LineartTriangle *tri;
LineartElementLinkNode *v_eln, *t_eln, *e_eln;
double(*vp)[4] = rb->view_projection;
int i;
@@ -1219,25 +1251,25 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
in0 = 0, in1 = 0, in2 = 0; \
if (clip_far) { \
/* Point outside far plane. */ \
- if (rt->v[0]->fbcoord[use_w] > clip_end) { \
+ if (tri->v[0]->fbcoord[use_w] > clip_end) { \
in0 = 1; \
} \
- if (rt->v[1]->fbcoord[use_w] > clip_end) { \
+ if (tri->v[1]->fbcoord[use_w] > clip_end) { \
in1 = 1; \
} \
- if (rt->v[2]->fbcoord[use_w] > clip_end) { \
+ if (tri->v[2]->fbcoord[use_w] > clip_end) { \
in2 = 1; \
} \
} \
else { \
/* Point inside near plane. */ \
- if (rt->v[0]->fbcoord[use_w] < clip_start) { \
+ if (tri->v[0]->fbcoord[use_w] < clip_start) { \
in0 = 1; \
} \
- if (rt->v[1]->fbcoord[use_w] < clip_start) { \
+ if (tri->v[1]->fbcoord[use_w] < clip_start) { \
in1 = 1; \
} \
- if (rt->v[2]->fbcoord[use_w] < clip_start) { \
+ if (tri->v[2]->fbcoord[use_w] < clip_start) { \
in2 = 1; \
} \
}
@@ -1252,19 +1284,19 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
}
/* Then go through all the other triangles. */
- LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
- if (reln->flags & LRT_ELEMENT_IS_ADDITIONAL) {
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) {
continue;
}
- ob = reln->object_ref;
- for (i = 0; i < reln->element_count; i++) {
+ ob = eln->object_ref;
+ for (i = 0; i < eln->element_count; i++) {
/* Select the triangle in the array. */
- rt = (void *)(((uchar *)reln->pointer) + rb->triangle_size * i);
+ tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i);
LRT_CULL_DECIDE_INSIDE
LRT_CULL_ENSURE_MEMORY
lineart_triangle_cull_single(rb,
- rt,
+ tri,
in0,
in1,
in2,
@@ -1298,40 +1330,40 @@ static void lineart_main_free_adjacent_data(LineartRenderBuffer *rb)
while ((ld = BLI_pophead(&rb->triangle_adjacent_pointers)) != NULL) {
MEM_freeN(ld->data);
}
- LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
- LineartTriangle *rt = reln->pointer;
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ LineartTriangle *tri = eln->pointer;
int i;
- for (i = 0; i < reln->element_count; i++) {
- /* See definition of rt->intersecting_verts and the usage in
+ for (i = 0; i < eln->element_count; i++) {
+ /* See definition of tri->intersecting_verts and the usage in
* lineart_geometry_object_load() for detailed. */
- rt->intersecting_verts = NULL;
- rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size);
+ tri->intersecting_verts = NULL;
+ tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size);
}
}
}
static void lineart_main_perspective_division(LineartRenderBuffer *rb)
{
- LineartVert *rv;
+ LineartVert *vt;
int i;
if (!rb->cam_is_persp) {
return;
}
- LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->vertex_buffer_pointers) {
- rv = reln->pointer;
- for (i = 0; i < reln->element_count; i++) {
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->vertex_buffer_pointers) {
+ vt = eln->pointer;
+ for (i = 0; i < eln->element_count; i++) {
/* Do not divide Z, we use Z to back transform cut points in later chaining process. */
- rv[i].fbcoord[0] /= rv[i].fbcoord[3];
- rv[i].fbcoord[1] /= rv[i].fbcoord[3];
+ vt[i].fbcoord[0] /= vt[i].fbcoord[3];
+ vt[i].fbcoord[1] /= vt[i].fbcoord[3];
/* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates)
* at the moment.
* The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed in
* the future, the line below correctly transforms it to view space coordinates. */
- // `rv[i].fbcoord[2] = -2 * rv[i].fbcoord[2] / (far - near) - (far + near) / (far - near);
- rv[i].fbcoord[0] -= rb->shift_x * 2;
- rv[i].fbcoord[1] -= rb->shift_y * 2;
+ // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near);
+ vt[i].fbcoord[0] -= rb->shift_x * 2;
+ vt[i].fbcoord[1] -= rb->shift_y * 2;
}
}
}
@@ -1343,10 +1375,10 @@ static void lineart_vert_transform(
BMVert *v, int index, LineartVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4])
{
double co[4];
- LineartVert *rv = &RvBuf[index];
+ LineartVert *vt = &RvBuf[index];
copy_v3db_v3fl(co, v->co);
- mul_v3_m4v3_db(rv->gloc, mv_mat, co);
- mul_v4_m4v3_db(rv->fbcoord, mvp_mat, co);
+ mul_v3_m4v3_db(vt->gloc, mv_mat, co);
+ mul_v4_m4v3_db(vt->fbcoord, mvp_mat, co);
}
/**
@@ -1381,12 +1413,12 @@ static char lineart_identify_feature_line(LineartRenderBuffer *rb,
return LRT_EDGE_FLAG_CONTOUR;
}
- LineartTriangle *rt1, *rt2;
+ LineartTriangle *tri1, *tri2;
LineartVert *l;
/* The mesh should already be triangulated now, so we can assume each face is a triangle. */
- rt1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f));
- rt2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f));
+ tri1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f));
+ tri2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f));
l = &rv_array[BM_elem_index_get(e->v1)];
@@ -1403,14 +1435,14 @@ static char lineart_identify_feature_line(LineartRenderBuffer *rb,
view_vector = rb->view_vector;
}
- dot_1 = dot_v3v3_db(view_vector, rt1->gn);
- dot_2 = dot_v3v3_db(view_vector, rt2->gn);
+ dot_1 = dot_v3v3_db(view_vector, tri1->gn);
+ dot_2 = dot_v3v3_db(view_vector, tri2->gn);
if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) {
return LRT_EDGE_FLAG_CONTOUR;
}
- if (rb->use_crease && (dot_v3v3_db(rt1->gn, rt2->gn) < crease_threshold)) {
+ if (rb->use_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) {
if (!no_crease) {
return LRT_EDGE_FLAG_CREASE;
}
@@ -1431,45 +1463,82 @@ static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e)
{
switch (e->flags) {
case LRT_EDGE_FLAG_CONTOUR:
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
break;
case LRT_EDGE_FLAG_CREASE:
- lineart_prepend_edge_direct(&rb->crease_lines, e);
+ lineart_prepend_edge_direct(&rb->crease.first, e);
break;
case LRT_EDGE_FLAG_MATERIAL:
- lineart_prepend_edge_direct(&rb->material_lines, e);
+ lineart_prepend_edge_direct(&rb->material.first, e);
break;
case LRT_EDGE_FLAG_EDGE_MARK:
- lineart_prepend_edge_direct(&rb->edge_marks, e);
+ lineart_prepend_edge_direct(&rb->edge_mark.first, e);
break;
case LRT_EDGE_FLAG_INTERSECTION:
- lineart_prepend_edge_direct(&rb->intersection_lines, e);
+ lineart_prepend_edge_direct(&rb->intersection.first, e);
break;
}
}
-static void lineart_triangle_adjacent_assign(LineartTriangle *rt,
- LineartTriangleAdjacent *rta,
+static void lineart_add_edge_to_list_thread(LineartObjectInfo *obi, LineartEdge *e)
+{
+
+#define LRT_ASSIGN_EDGE(name) \
+ lineart_prepend_edge_direct(&obi->name.first, e); \
+ if (!obi->name.last) { \
+ obi->name.last = e; \
+ }
+ switch (e->flags) {
+ case LRT_EDGE_FLAG_CONTOUR:
+ LRT_ASSIGN_EDGE(contour);
+ break;
+ case LRT_EDGE_FLAG_CREASE:
+ LRT_ASSIGN_EDGE(crease);
+ break;
+ case LRT_EDGE_FLAG_MATERIAL:
+ LRT_ASSIGN_EDGE(material);
+ break;
+ case LRT_EDGE_FLAG_EDGE_MARK:
+ LRT_ASSIGN_EDGE(edge_mark);
+ break;
+ case LRT_EDGE_FLAG_INTERSECTION:
+ LRT_ASSIGN_EDGE(intersection);
+ break;
+ }
+#undef LRT_ASSIGN_EDGE
+}
+
+static void lineart_finalize_object_edge_list(LineartRenderBuffer *rb, LineartObjectInfo *obi)
+{
+#define LRT_OBI_TO_RB(name) \
+ if (obi->name.last) { \
+ ((LineartEdge *)obi->name.last)->next = rb->name.first; \
+ rb->name.first = obi->name.first; \
+ }
+ LRT_OBI_TO_RB(contour);
+ LRT_OBI_TO_RB(crease);
+ LRT_OBI_TO_RB(material);
+ LRT_OBI_TO_RB(edge_mark);
+ LRT_OBI_TO_RB(intersection);
+#undef LRT_OBI_TO_RB
+}
+
+static void lineart_triangle_adjacent_assign(LineartTriangle *tri,
+ LineartTriangleAdjacent *ta,
LineartEdge *e)
{
- if (lineart_edge_match(rt, e, 0, 1)) {
- rta->e[0] = e;
+ if (lineart_edge_match(tri, e, 0, 1)) {
+ ta->e[0] = e;
}
- else if (lineart_edge_match(rt, e, 1, 2)) {
- rta->e[1] = e;
+ else if (lineart_edge_match(tri, e, 1, 2)) {
+ ta->e[1] = e;
}
- else if (lineart_edge_match(rt, e, 2, 0)) {
- rta->e[2] = e;
+ else if (lineart_edge_match(tri, e, 2, 0)) {
+ ta->e[2] = e;
}
}
-static void lineart_geometry_object_load(Depsgraph *dg,
- Object *ob,
- double (*mv_mat)[4],
- double (*mvp_mat)[4],
- LineartRenderBuffer *rb,
- int override_usage,
- int *global_vindex)
+static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb)
{
BMesh *bm;
BMVert *v;
@@ -1477,252 +1546,244 @@ static void lineart_geometry_object_load(Depsgraph *dg,
BMEdge *e;
BMLoop *loop;
LineartEdge *la_e;
- LineartTriangle *rt;
+ LineartEdgeSegment *la_s;
+ LineartTriangle *tri;
LineartTriangleAdjacent *orta;
- double new_mvp[4][4], new_mv[4][4], normal[4][4];
- float imat[4][4];
- LineartElementLinkNode *reln;
+ double(*model_view_proj)[4] = obi->model_view_proj, (*model_view)[4] = obi->model_view,
+ (*normal)[4] = obi->normal;
+ LineartElementLinkNode *eln;
LineartVert *orv;
LineartEdge *o_la_e;
+ LineartEdgeSegment *o_la_s;
LineartTriangle *ort;
Object *orig_ob;
int CanFindFreestyle = 0;
- int i, global_i = (*global_vindex);
- Mesh *use_mesh;
+ int i;
float use_crease = 0;
- int usage = override_usage ? override_usage : ob->lineart.usage;
+ int usage = obi->usage;
-#define LRT_MESH_FINISH \
- BM_mesh_free(bm); \
- if (ob->type != OB_MESH) { \
- BKE_mesh_free(use_mesh); \
- MEM_freeN(use_mesh); \
+ if (obi->original_me->edit_mesh) {
+ /* Do not use edit_mesh directly because we will modify it, so create a copy. */
+ bm = BM_mesh_copy(obi->original_me->edit_mesh->bm);
+ }
+ else {
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(obi->original_me)));
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+ BM_mesh_bm_from_me(bm,
+ obi->original_me,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
}
- if (usage == OBJECT_LRT_EXCLUDE) {
- return;
+ if (obi->free_use_mesh) {
+ BKE_mesh_free(obi->original_me);
+ MEM_freeN(obi->original_me);
}
- if (ob->type == OB_MESH || ob->type == OB_MBALL || ob->type == OB_CURVE || ob->type == OB_SURF ||
- ob->type == OB_FONT) {
+ if (rb->remove_doubles) {
+ BMEditMesh *em = BKE_editmesh_create(bm, false);
+ BMOperator findop, weldop;
- if (ob->type == OB_MESH) {
- use_mesh = DEG_get_evaluated_object(dg, ob)->data;
- }
- else {
- use_mesh = BKE_mesh_new_from_object(NULL, ob, false);
- }
+ /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */
+ BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001);
- /* In case we can not get any mesh geometry data from the object */
- if (!use_mesh) {
- return;
- }
+ BMO_op_exec(bm, &findop);
- /* First we need to prepare the matrix used for transforming this specific object. */
- mul_m4db_m4db_m4fl_uniq(new_mvp, mvp_mat, ob->obmat);
- mul_m4db_m4db_m4fl_uniq(new_mv, mv_mat, ob->obmat);
+ /* Weld the vertices. */
+ BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts");
+ BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap");
+ BMO_op_exec(bm, &weldop);
- invert_m4_m4(imat, ob->obmat);
- transpose_m4(imat);
- copy_m4d_m4(normal, imat);
+ BMO_op_finish(bm, &findop);
+ BMO_op_finish(bm, &weldop);
- if (use_mesh->edit_mesh) {
- /* Do not use edit_mesh directly because we will modify it, so create a copy. */
- bm = BM_mesh_copy(use_mesh->edit_mesh->bm);
- }
- else {
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(use_mesh)));
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(bm,
- use_mesh,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
- }
+ MEM_freeN(em);
+ }
+
+ BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false);
+ BM_mesh_triangulate(
+ bm, MOD_TRIANGULATE_QUAD_FIXED, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL);
+ BM_mesh_normals_update(bm);
+ BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
+ CanFindFreestyle = 1;
+ }
- if (rb->remove_doubles) {
- BMEditMesh *em = BKE_editmesh_create(bm, false);
- BMOperator findop, weldop;
+ /* Only allocate memory for verts and tris as we don't know how many lines we will generate
+ * yet. */
+ orv = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert);
+ ort = lineart_mem_acquire_thread(&rb->render_data_pool, bm->totface * rb->triangle_size);
- /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */
- BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001);
+ orig_ob = obi->original_ob;
- BMO_op_exec(bm, &findop);
+ BLI_spin_lock(&rb->lock_task);
+ eln = lineart_list_append_pointer_pool_sized_thread(
+ &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&rb->lock_task);
- /* Weld the vertices. */
- BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts");
- BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap");
- BMO_op_exec(bm, &weldop);
+ eln->element_count = bm->totvert;
+ eln->object_ref = orig_ob;
+ obi->v_reln = eln;
- BMO_op_finish(bm, &findop);
- BMO_op_finish(bm, &weldop);
+ if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
+ use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold);
+ }
+ else {
+ use_crease = rb->crease_threshold;
+ }
- MEM_freeN(em);
- }
+ /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates
+ * erroneous detection on creases. Future configuration should allow options. */
+ if (orig_ob->type == OB_FONT) {
+ eln->flags |= LRT_ELEMENT_BORDER_ONLY;
+ }
- BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false);
- BM_mesh_triangulate(
- bm, MOD_TRIANGULATE_QUAD_FIXED, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL);
- BM_mesh_normals_update(bm);
- BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
- BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ BLI_spin_lock(&rb->lock_task);
+ eln = lineart_list_append_pointer_pool_sized_thread(
+ &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&rb->lock_task);
- if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
- CanFindFreestyle = 1;
- }
+ eln->element_count = bm->totface;
+ eln->object_ref = orig_ob;
+ eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0);
- /* Only allocate memory for verts and tris as we don't know how many lines we will generate
- * yet. */
- orv = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert);
- ort = lineart_mem_aquire(&rb->render_data_pool, bm->totface * rb->triangle_size);
+ /* Note this memory is not from pool, will be deleted after culling. */
+ orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent");
+ /* Link is minimal so we use pool anyway. */
+ BLI_spin_lock(&rb->lock_task);
+ lineart_list_append_pointer_pool_thread(
+ &rb->triangle_adjacent_pointers, &rb->render_data_pool, orta);
+ BLI_spin_unlock(&rb->lock_task);
- orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob;
+ for (i = 0; i < bm->totvert; i++) {
+ v = BM_vert_at_index(bm, i);
+ lineart_vert_transform(v, i, orv, model_view, model_view_proj);
+ orv[i].index = i;
+ }
+ /* Register a global index increment. See #lineart_triangle_share_edge() and
+ * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually
+ * overflow, in such large scene it's virtually impossible for two vertex of the same numeric
+ * index to come close together. */
+ obi->global_i_offset = bm->totvert;
- reln = lineart_list_append_pointer_pool_sized(
- &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode));
- reln->element_count = bm->totvert;
- reln->object_ref = orig_ob;
+ tri = ort;
+ for (i = 0; i < bm->totface; i++) {
+ f = BM_face_at_index(bm, i);
- if (ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
- use_crease = cosf(M_PI - ob->lineart.crease_threshold);
- }
- else {
- use_crease = rb->crease_threshold;
- }
-
- /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates
- * erroneous detection on creases. Future configuration should allow options. */
- if (ob->type == OB_FONT) {
- reln->flags |= LRT_ELEMENT_BORDER_ONLY;
- }
-
- reln = lineart_list_append_pointer_pool_sized(
- &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode));
- reln->element_count = bm->totface;
- reln->object_ref = orig_ob;
- reln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0);
-
- /* Note this memory is not from pool, will be deleted after culling. */
- orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent");
- /* Link is minimal so we use pool anyway. */
- lineart_list_append_pointer_pool(&rb->triangle_adjacent_pointers, &rb->render_data_pool, orta);
-
- for (i = 0; i < bm->totvert; i++) {
- v = BM_vert_at_index(bm, i);
- lineart_vert_transform(v, i, orv, new_mv, new_mvp);
- orv[i].index = i + global_i;
- }
- /* Register a global index increment. See #lineart_triangle_share_edge() and
- * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually
- * overflow, in such large scene it's virtually impossible for two vertex of the same numeric
- * index to come close together. */
- (*global_vindex) += bm->totvert;
-
- rt = ort;
- for (i = 0; i < bm->totface; i++) {
- f = BM_face_at_index(bm, i);
-
- loop = f->l_first;
- rt->v[0] = &orv[BM_elem_index_get(loop->v)];
- loop = loop->next;
- rt->v[1] = &orv[BM_elem_index_get(loop->v)];
- loop = loop->next;
- rt->v[2] = &orv[BM_elem_index_get(loop->v)];
-
- /* Transparency bit assignment. */
- Material *mat = BKE_object_material_get(ob, f->mat_nr + 1);
- rt->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ?
- mat->lineart.transparency_mask :
- 0);
-
- double gn[3];
- copy_v3db_v3fl(gn, f->no);
- mul_v3_mat3_m4v3_db(rt->gn, normal, gn);
- normalize_v3_db(rt->gn);
-
- if (usage == OBJECT_LRT_INTERSECTION_ONLY) {
- rt->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
- }
- else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) {
- rt->flags |= LRT_TRIANGLE_NO_INTERSECTION;
- }
+ loop = f->l_first;
+ tri->v[0] = &orv[BM_elem_index_get(loop->v)];
+ loop = loop->next;
+ tri->v[1] = &orv[BM_elem_index_get(loop->v)];
+ loop = loop->next;
+ tri->v[2] = &orv[BM_elem_index_get(loop->v)];
+
+ /* Transparency bit assignment. */
+ Material *mat = BKE_object_material_get(orig_ob, f->mat_nr + 1);
+ tri->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ?
+ mat->lineart.transparency_mask :
+ 0);
- /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
- rt->intersecting_verts = (void *)&orta[i];
+ double gn[3];
+ copy_v3db_v3fl(gn, f->no);
+ mul_v3_mat3_m4v3_db(tri->gn, normal, gn);
+ normalize_v3_db(tri->gn);
- rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size);
+ if (usage == OBJECT_LRT_INTERSECTION_ONLY) {
+ tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
}
+ else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) {
+ tri->flags |= LRT_TRIANGLE_NO_INTERSECTION;
+ }
+
+ /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
+ tri->intersecting_verts = (void *)&orta[i];
- /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */
+ tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size);
+ }
- int allocate_la_e = 0;
- for (i = 0; i < bm->totedge; i++) {
- e = BM_edge_at_index(bm, i);
+ /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */
- /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */
- char eflag = lineart_identify_feature_line(
- rb, e, ort, orv, use_crease, ob->type == OB_FONT, CanFindFreestyle, bm);
- if (eflag) {
- /* Only allocate for feature lines (instead of all lines) to save memory. */
- allocate_la_e++;
- }
- /* Here we just use bm's flag for when loading actual lines, then we don't need to call
- * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always
- * set the flag, so hflag stays 0 for lines that are not feature lines. */
- e->head.hflag = eflag;
+ int allocate_la_e = 0;
+ for (i = 0; i < bm->totedge; i++) {
+ e = BM_edge_at_index(bm, i);
+
+ /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */
+ char eflag = lineart_identify_feature_line(
+ rb, e, ort, orv, use_crease, orig_ob->type == OB_FONT, CanFindFreestyle, bm);
+ if (eflag) {
+ /* Only allocate for feature lines (instead of all lines) to save memory. */
+ allocate_la_e++;
}
+ /* Here we just use bm's flag for when loading actual lines, then we don't need to call
+ * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always
+ * set the flag, so hflag stays 0 for lines that are not feature lines. */
+ e->head.hflag = eflag;
+ }
- o_la_e = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
- reln = lineart_list_append_pointer_pool_sized(
- &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode));
- reln->element_count = allocate_la_e;
- reln->object_ref = orig_ob;
+ o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
+ o_la_s = lineart_mem_acquire_thread(&rb->render_data_pool,
+ sizeof(LineartEdgeSegment) * allocate_la_e);
+ BLI_spin_lock(&rb->lock_task);
+ eln = lineart_list_append_pointer_pool_sized_thread(
+ &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&rb->lock_task);
+ eln->element_count = allocate_la_e;
+ eln->object_ref = orig_ob;
- la_e = o_la_e;
- for (i = 0; i < bm->totedge; i++) {
- e = BM_edge_at_index(bm, i);
+ la_e = o_la_e;
+ la_s = o_la_s;
+ for (i = 0; i < bm->totedge; i++) {
+ e = BM_edge_at_index(bm, i);
- /* Not a feature line, so we skip. */
- if (!e->head.hflag) {
- continue;
- }
+ /* Not a feature line, so we skip. */
+ if (!e->head.hflag) {
+ continue;
+ }
- la_e->v1 = &orv[BM_elem_index_get(e->v1)];
- la_e->v2 = &orv[BM_elem_index_get(e->v2)];
- la_e->v1_obindex = la_e->v1->index - global_i;
- la_e->v2_obindex = la_e->v2->index - global_i;
- if (e->l) {
- int findex = BM_elem_index_get(e->l->f);
- la_e->t1 = lineart_triangle_from_index(rb, ort, findex);
- lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e);
- if (e->l->radial_next && e->l->radial_next != e->l) {
- findex = BM_elem_index_get(e->l->radial_next->f);
- la_e->t2 = lineart_triangle_from_index(rb, ort, findex);
- lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e);
- }
- }
- la_e->flags = e->head.hflag;
- la_e->object_ref = orig_ob;
-
- LineartLineSegment *rls = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartLineSegment));
- BLI_addtail(&la_e->segments, rls);
- if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
- usage == OBJECT_LRT_NO_INTERSECTION) {
- lineart_add_edge_to_list(rb, la_e);
+ la_e->v1 = &orv[BM_elem_index_get(e->v1)];
+ la_e->v2 = &orv[BM_elem_index_get(e->v2)];
+ la_e->v1_obindex = la_e->v1->index;
+ la_e->v2_obindex = la_e->v2->index;
+ if (e->l) {
+ int findex = BM_elem_index_get(e->l->f);
+ la_e->t1 = lineart_triangle_from_index(rb, ort, findex);
+ lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e);
+ if (e->l->radial_next && e->l->radial_next != e->l) {
+ findex = BM_elem_index_get(e->l->radial_next->f);
+ la_e->t2 = lineart_triangle_from_index(rb, ort, findex);
+ lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e);
}
-
- la_e++;
+ }
+ la_e->flags = e->head.hflag;
+ la_e->object_ref = orig_ob;
+ BLI_addtail(&la_e->segments, la_s);
+ if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
+ usage == OBJECT_LRT_NO_INTERSECTION) {
+ lineart_add_edge_to_list_thread(obi, la_e);
}
- LRT_MESH_FINISH
+ la_e++;
+ la_s++;
}
-#undef LRT_MESH_FINISH
+ /* always free bm as it's a copy from before threading */
+ BM_mesh_free(bm);
+}
+
+static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool),
+ LineartObjectLoadTaskInfo *olti)
+{
+ LineartRenderBuffer *rb = olti->rb;
+ for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) {
+ lineart_geometry_object_load(obi, rb);
+ }
}
static bool _lineart_object_not_in_source_collection(Collection *source, Object *ob)
@@ -1802,6 +1863,24 @@ static int lineart_usage_check(Collection *c, Object *ob, LineartRenderBuffer *_
return OBJECT_LRT_INHERIT;
}
+static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_list,
+ LineartObjectInfo *obi,
+ int thread_count,
+ int this_face_count)
+{
+ LineartObjectLoadTaskInfo *use_olti = olti_list;
+ long unsigned int min_face = use_olti->total_faces;
+ for (int i = 0; i < thread_count; i++) {
+ if (olti_list[i].total_faces < min_face) {
+ min_face = olti_list[i].total_faces;
+ use_olti = &olti_list[i];
+ }
+ }
+ use_olti->total_faces += this_face_count;
+ obi->next = use_olti->pending;
+ use_olti->pending = obi;
+}
+
static void lineart_main_load_geometries(
Depsgraph *depsgraph,
Scene *scene,
@@ -1814,26 +1893,37 @@ static void lineart_main_load_geometries(
Camera *cam = camera->data;
float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
- double fov = focallength_to_fov(cam->lens, sensor);
-
+ int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h);
double asp = ((double)rb->w / (double)rb->h);
+ double t_start;
+
+ if (G.debug_value == 4000) {
+ t_start = PIL_check_seconds_timer();
+ }
+
if (cam->type == CAM_PERSP) {
- if (asp < 1) {
- fov /= asp;
+ if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) {
+ sensor *= asp;
+ }
+ if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
+ sensor /= asp;
}
+ double fov = focallength_to_fov(cam->lens, sensor);
lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
}
else if (cam->type == CAM_ORTHO) {
double w = cam->ortho_scale / 2;
lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
}
+
invert_m4_m4(inv, rb->cam_obmat);
mul_m4db_m4db_m4fl_uniq(result, proj, inv);
copy_m4_m4_db(proj, result);
copy_m4_m4_db(rb->view_projection, proj);
unit_m4_db(view);
+ copy_m4_m4_db(rb->view, view);
BLI_listbase_clear(&rb->triangle_buffer_pointers);
BLI_listbase_clear(&rb->vertex_buffer_pointers);
@@ -1846,67 +1936,141 @@ static void lineart_main_load_geometries(
flags |= DEG_ITER_OBJECT_FLAG_DUPLI;
}
- /* This is to serialize vertex index in the whole scene, so lineart_triangle_share_edge() can
- * work properly from the lack of triangle adjacent info. */
- int global_i = 0;
+ int thread_count = rb->thread_count;
+
+ /* This memory is in render buffer memory pool. so we don't need to free those after loading. */
+ LineartObjectLoadTaskInfo *olti = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count);
DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) {
- int usage = lineart_usage_check(scene->master_collection, ob, rb);
+ LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo));
+ obi->usage = lineart_usage_check(scene->master_collection, ob, rb);
+ Mesh *use_mesh;
+
+ if (obi->usage == OBJECT_LRT_EXCLUDE) {
+ continue;
+ }
+
+ Object *use_ob = DEG_get_evaluated_object(depsgraph, ob);
+
+ if (!(use_ob->type == OB_MESH || use_ob->type == OB_MBALL || use_ob->type == OB_CURVE ||
+ use_ob->type == OB_SURF || use_ob->type == OB_FONT)) {
+ continue;
+ }
+ if (use_ob->type == OB_MESH) {
+ use_mesh = use_ob->data;
+ }
+ else {
+ use_mesh = BKE_mesh_new_from_object(NULL, use_ob, false, true);
+ }
+
+ /* In case we still can not get any mesh geometry data from the object */
+ if (!use_mesh) {
+ continue;
+ }
+
+ if (ob->type != OB_MESH) {
+ obi->free_use_mesh = true;
+ }
- lineart_geometry_object_load(depsgraph, ob, view, proj, rb, usage, &global_i);
+ /* Prepare the matrix used for transforming this specific object (instance). */
+ mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat);
+ mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat);
+ float imat[4][4];
+ invert_m4_m4(imat, ob->obmat);
+ transpose_m4(imat);
+ copy_m4d_m4(obi->normal, imat);
+
+ obi->original_me = use_mesh;
+ obi->original_ob = (ob->id.orig_id ? (Object *)ob->id.orig_id : (Object *)ob);
+ lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly);
}
DEG_OBJECT_ITER_END;
+
+ TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
+
+ for (int i = 0; i < thread_count; i++) {
+ olti[i].rb = rb;
+ olti[i].dg = depsgraph;
+ BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL);
+ }
+ BLI_task_pool_work_and_wait(tp);
+ BLI_task_pool_free(tp);
+
+ /* The step below is to serialize vertex index in the whole scene, so
+ * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */
+ int global_i = 0;
+
+ for (int i = 0; i < thread_count; i++) {
+ for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) {
+ if (!obi->v_reln) {
+ continue;
+ }
+ LineartVert *v = (LineartVert *)obi->v_reln->pointer;
+ int v_count = obi->v_reln->element_count;
+ for (int vi = 0; vi < v_count; vi++) {
+ v[vi].index += global_i;
+ }
+ global_i += v_count;
+ lineart_finalize_object_edge_list(rb, obi);
+ }
+ }
+
+ if (G.debug_value == 4000) {
+ double t_elapsed = PIL_check_seconds_timer() - t_start;
+ printf("Line art loading time: %lf\n", t_elapsed);
+ }
}
/**
* Returns the two other verts of the triangle given a vertex. Returns false if the given vertex
* doesn't belong to this triangle.
*/
-static bool lineart_triangle_get_other_verts(const LineartTriangle *rt,
- const LineartVert *rv,
+static bool lineart_triangle_get_other_verts(const LineartTriangle *tri,
+ const LineartVert *vt,
LineartVert **l,
LineartVert **r)
{
- if (rt->v[0] == rv) {
- *l = rt->v[1];
- *r = rt->v[2];
+ if (tri->v[0] == vt) {
+ *l = tri->v[1];
+ *r = tri->v[2];
return true;
}
- if (rt->v[1] == rv) {
- *l = rt->v[2];
- *r = rt->v[0];
+ if (tri->v[1] == vt) {
+ *l = tri->v[2];
+ *r = tri->v[0];
return true;
}
- if (rt->v[2] == rv) {
- *l = rt->v[0];
- *r = rt->v[1];
+ if (tri->v[2] == vt) {
+ *l = tri->v[0];
+ *r = tri->v[1];
return true;
}
return false;
}
-static bool lineart_edge_from_triangle(const LineartTriangle *rt,
+static bool lineart_edge_from_triangle(const LineartTriangle *tri,
const LineartEdge *e,
bool allow_overlapping_edges)
{
/* Normally we just determine from the pointer address. */
- if (e->t1 == rt || e->t2 == rt) {
+ if (e->t1 == tri || e->t2 == tri) {
return true;
}
/* If allows overlapping, then we compare the vertex coordinates one by one to determine if one
* edge is from specific triangle. This is slower but can handle edge split cases very well. */
if (allow_overlapping_edges) {
-#define LRT_TRI_SAME_POINT(rt, i, pt) \
- ((LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \
- LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \
- LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2])) || \
- (LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \
- LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \
- LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2])))
- if ((LRT_TRI_SAME_POINT(rt, 0, e->v1) || LRT_TRI_SAME_POINT(rt, 1, e->v1) ||
- LRT_TRI_SAME_POINT(rt, 2, e->v1)) &&
- (LRT_TRI_SAME_POINT(rt, 0, e->v2) || LRT_TRI_SAME_POINT(rt, 1, e->v2) ||
- LRT_TRI_SAME_POINT(rt, 2, e->v2))) {
+#define LRT_TRI_SAME_POINT(tri, i, pt) \
+ ((LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])) || \
+ (LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])))
+ if ((LRT_TRI_SAME_POINT(tri, 0, e->v1) || LRT_TRI_SAME_POINT(tri, 1, e->v1) ||
+ LRT_TRI_SAME_POINT(tri, 2, e->v1)) &&
+ (LRT_TRI_SAME_POINT(tri, 0, e->v2) || LRT_TRI_SAME_POINT(tri, 1, e->v2) ||
+ LRT_TRI_SAME_POINT(tri, 2, e->v2))) {
return true;
}
#undef LRT_TRI_SAME_POINT
@@ -1948,7 +2112,7 @@ static bool lineart_edge_from_triangle(const LineartTriangle *rt,
* in ratio from `e->v1` to `e->v2`. The line is later cut with these two values.
*/
static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
- const LineartTriangle *rt,
+ const LineartTriangle *tri,
const LineartEdge *e,
const double *override_camera_loc,
const bool override_cam_is_persp,
@@ -1975,8 +2139,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
double gloc[4], trans[4];
double cut = -1;
- double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = rt->v[0]->fbcoord,
- *FBC1 = rt->v[1]->fbcoord, *FBC2 = rt->v[2]->fbcoord;
+ double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = tri->v[0]->fbcoord,
+ *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord;
/* Overlapping not possible, return early. */
if ((MAX3(FBC0[0], FBC1[0], FBC2[0]) < MIN2(LFBC[0], RFBC[0])) ||
@@ -1988,7 +2152,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
}
/* If the the line is one of the edge in the triangle, then it's not occluded. */
- if (lineart_edge_from_triangle(rt, e, allow_overlapping_edges)) {
+ if (lineart_edge_from_triangle(tri, e, allow_overlapping_edges)) {
return false;
}
@@ -2000,8 +2164,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
/* Sort the intersection distance. */
INTERSECT_SORT_MIN_TO_MAX_3(is[0], is[1], is[2], order);
- sub_v3_v3v3_db(Lv, e->v1->gloc, rt->v[0]->gloc);
- sub_v3_v3v3_db(Rv, e->v2->gloc, rt->v[0]->gloc);
+ sub_v3_v3v3_db(Lv, e->v1->gloc, tri->v[0]->gloc);
+ sub_v3_v3v3_db(Rv, e->v2->gloc, tri->v[0]->gloc);
copy_v3_v3_db(Cv, camera_dir);
@@ -2012,14 +2176,18 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
copy_v4_v4_db(vd4, override_camera_loc);
}
if (override_cam_is_persp) {
- sub_v3_v3v3_db(Cv, vd4, rt->v[0]->gloc);
+ sub_v3_v3v3_db(Cv, vd4, tri->v[0]->gloc);
}
- dot_l = dot_v3v3_db(Lv, rt->gn);
- dot_r = dot_v3v3_db(Rv, rt->gn);
- dot_f = dot_v3v3_db(Cv, rt->gn);
+ dot_l = dot_v3v3_db(Lv, tri->gn);
+ dot_r = dot_v3v3_db(Rv, tri->gn);
+ dot_f = dot_v3v3_db(Cv, tri->gn);
- if (!dot_f) {
+ /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_
+ * faces in perspective mode would get erroneously caught in this condition where they really are
+ * legit faces that would produce occlusion, but haven't encountered those yet in my test files.
+ */
+ if (fabs(dot_f) < FLT_EPSILON) {
return false;
}
@@ -2258,17 +2426,17 @@ static LineartVert *lineart_triangle_share_point(const LineartTriangle *l,
/**
* To save time and prevent overlapping lines when computing intersection lines.
*/
-static bool lineart_vert_already_intersected_2v(LineartVertIntersection *rv,
+static bool lineart_vert_already_intersected_2v(LineartVertIntersection *vt,
LineartVertIntersection *v1,
LineartVertIntersection *v2)
{
- return ((rv->isec1 == v1->base.index && rv->isec2 == v2->base.index) ||
- (rv->isec2 == v2->base.index && rv->isec1 == v1->base.index));
+ return ((vt->isec1 == v1->base.index && vt->isec2 == v2->base.index) ||
+ (vt->isec2 == v2->base.index && vt->isec1 == v1->base.index));
}
-static void lineart_vert_set_intersection_2v(LineartVert *rv, LineartVert *v1, LineartVert *v2)
+static void lineart_vert_set_intersection_2v(LineartVert *vt, LineartVert *v1, LineartVert *v2)
{
- LineartVertIntersection *irv = (LineartVertIntersection *)rv;
+ LineartVertIntersection *irv = (LineartVertIntersection *)vt;
irv->isec1 = v1->index;
irv->isec2 = v2->index;
}
@@ -2281,7 +2449,7 @@ static void lineart_vert_set_intersection_2v(LineartVert *rv, LineartVert *v1, L
static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *rb,
LineartVert *v1,
LineartVert *v2,
- LineartTriangle *rt,
+ LineartTriangle *tri,
LineartTriangle *testing,
LineartVert *last)
{
@@ -2293,11 +2461,11 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r
LineartVert *l = v1, *r = v2;
for (LinkNode *ln = (void *)testing->intersecting_verts; ln; ln = ln->next) {
- LineartVertIntersection *rv = ln->link;
- if (rv->intersecting_with == rt &&
+ LineartVertIntersection *vt = ln->link;
+ if (vt->intersecting_with == tri &&
lineart_vert_already_intersected_2v(
- rv, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) {
- return (LineartVert *)rv;
+ vt, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) {
+ return (LineartVert *)vt;
}
}
@@ -2331,7 +2499,7 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r
/* This is an intersection vert, the size is bigger than LineartVert,
* allocated separately. */
- result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartVertIntersection));
+ result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVertIntersection));
/* Indicate the data structure difference. */
result->flag = LRT_VERT_HAS_INTERSECTION_DATA;
@@ -2347,7 +2515,7 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r
* Test if two triangles intersect. Generates one intersection line if the check succeeds.
*/
static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
LineartTriangle *testing)
{
LineartVert *v1 = 0, *v2 = 0;
@@ -2366,62 +2534,62 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
ZMax = rb->far_clip;
ZMin = rb->near_clip;
copy_v3_v3_db(cl, rb->camera_pos);
- LineartVert *share = lineart_triangle_share_point(testing, rt);
+ LineartVert *share = lineart_triangle_share_point(testing, tri);
if (share) {
/* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc`
* against `acd` or `cd` against `abc`. */
LineartVert *new_share;
- lineart_triangle_get_other_verts(rt, share, &sv1, &sv2);
+ lineart_triangle_get_other_verts(tri, share, &sv1, &sv2);
- v1 = new_share = lineart_mem_aquire(&rb->render_data_pool, (sizeof(LineartVertIntersection)));
+ v1 = new_share = lineart_mem_acquire(&rb->render_data_pool, (sizeof(LineartVertIntersection)));
new_share->flag = LRT_VERT_HAS_INTERSECTION_DATA;
copy_v3_v3_db(new_share->gloc, share->gloc);
- v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, rt, testing, 0);
+ v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, tri, testing, 0);
if (v2 == NULL) {
lineart_triangle_get_other_verts(testing, share, &sv1, &sv2);
- v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, rt, 0);
+ v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, tri, 0);
if (v2 == NULL) {
return 0;
}
lineart_prepend_pool(&testing->intersecting_verts, &rb->render_data_pool, new_share);
}
else {
- lineart_prepend_pool(&rt->intersecting_verts, &rb->render_data_pool, new_share);
+ lineart_prepend_pool(&tri->intersecting_verts, &rb->render_data_pool, new_share);
}
}
else {
/* If not sharing any points, then we need to try all the possibilities. */
- E0T = lineart_triangle_2v_intersection_test(rb, rt->v[0], rt->v[1], rt, testing, 0);
+ E0T = lineart_triangle_2v_intersection_test(rb, tri->v[0], tri->v[1], tri, testing, 0);
if (E0T && (!(*next))) {
(*next) = E0T;
- lineart_vert_set_intersection_2v((*next), rt->v[0], rt->v[1]);
+ lineart_vert_set_intersection_2v((*next), tri->v[0], tri->v[1]);
next = &v2;
}
- E1T = lineart_triangle_2v_intersection_test(rb, rt->v[1], rt->v[2], rt, testing, v1);
+ E1T = lineart_triangle_2v_intersection_test(rb, tri->v[1], tri->v[2], tri, testing, v1);
if (E1T && (!(*next))) {
(*next) = E1T;
- lineart_vert_set_intersection_2v((*next), rt->v[1], rt->v[2]);
+ lineart_vert_set_intersection_2v((*next), tri->v[1], tri->v[2]);
next = &v2;
}
if (!(*next)) {
- E2T = lineart_triangle_2v_intersection_test(rb, rt->v[2], rt->v[0], rt, testing, v1);
+ E2T = lineart_triangle_2v_intersection_test(rb, tri->v[2], tri->v[0], tri, testing, v1);
}
if (E2T && (!(*next))) {
(*next) = E2T;
- lineart_vert_set_intersection_2v((*next), rt->v[2], rt->v[0]);
+ lineart_vert_set_intersection_2v((*next), tri->v[2], tri->v[0]);
next = &v2;
}
if (!(*next)) {
TE0 = lineart_triangle_2v_intersection_test(
- rb, testing->v[0], testing->v[1], testing, rt, v1);
+ rb, testing->v[0], testing->v[1], testing, tri, v1);
}
if (TE0 && (!(*next))) {
(*next) = TE0;
@@ -2430,7 +2598,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
}
if (!(*next)) {
TE1 = lineart_triangle_2v_intersection_test(
- rb, testing->v[1], testing->v[2], testing, rt, v1);
+ rb, testing->v[1], testing->v[2], testing, tri, v1);
}
if (TE1 && (!(*next))) {
(*next) = TE1;
@@ -2439,7 +2607,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
}
if (!(*next)) {
TE2 = lineart_triangle_2v_intersection_test(
- rb, testing->v[2], testing->v[0], testing, rt, v1);
+ rb, testing->v[2], testing->v[0], testing, tri, v1);
}
if (TE2 && (!(*next))) {
(*next) = TE2;
@@ -2471,70 +2639,66 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin));
v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin));
- ((LineartVertIntersection *)v1)->intersecting_with = rt;
+ ((LineartVertIntersection *)v1)->intersecting_with = tri;
((LineartVertIntersection *)v2)->intersecting_with = testing;
- result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge));
+ result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge));
result->v1 = v1;
result->v2 = v2;
- result->t1 = rt;
+ result->t1 = tri;
result->t2 = testing;
- LineartLineSegment *rls = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineSegment));
- BLI_addtail(&result->segments, rls);
+ LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment));
+ BLI_addtail(&result->segments, es);
/* Don't need to OR flags right now, just a type mark. */
result->flags = LRT_EDGE_FLAG_INTERSECTION;
- lineart_prepend_edge_direct(&rb->intersection_lines, result);
+
+ lineart_prepend_edge_direct(&rb->intersection.first, result);
int r1, r2, c1, c2, row, col;
if (lineart_get_edge_bounding_areas(rb, result, &r1, &r2, &c1, &c2)) {
for (row = r1; row != r2 + 1; row++) {
for (col = c1; col != c2 + 1; col++) {
- lineart_bounding_area_link_line(
+ lineart_bounding_area_link_edge(
rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], result);
}
}
}
-
- rb->intersection_count++;
-
return result;
}
static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
LineartBoundingArea *ba)
{
/* Testing_triangle->testing[0] is used to store pairing triangle reference.
* See definition of LineartTriangleThread for more info. */
LineartTriangle *testing_triangle;
- LineartTriangleThread *rtt;
- LinkData *lip, *next_lip;
+ LineartTriangleThread *tt;
- double *G0 = rt->v[0]->gloc, *G1 = rt->v[1]->gloc, *G2 = rt->v[2]->gloc;
+ double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc;
/* If this is not the smallest subdiv bounding area.*/
if (ba->child) {
- lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[0]);
- lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[1]);
- lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[2]);
- lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[3]);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[2]);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[3]);
return;
}
/* If this _is_ the smallest subdiv bounding area, then do the intersections there. */
- for (lip = ba->linked_triangles.first; lip; lip = next_lip) {
- next_lip = lip->next;
- testing_triangle = lip->data;
- rtt = (LineartTriangleThread *)testing_triangle;
+ for (int i = 0; i < ba->triangle_count; i++) {
+ testing_triangle = ba->linked_triangles[i];
+ tt = (LineartTriangleThread *)testing_triangle;
- if (testing_triangle == rt || rtt->testing_e[0] == (LineartEdge *)rt) {
+ if (testing_triangle == tri || tt->testing_e[0] == (LineartEdge *)tri) {
continue;
}
- rtt->testing_e[0] = (LineartEdge *)rt;
+ tt->testing_e[0] = (LineartEdge *)tri;
if ((testing_triangle->flags & LRT_TRIANGLE_NO_INTERSECTION) ||
((testing_triangle->flags & LRT_TRIANGLE_INTERSECTION_ONLY) &&
- (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) {
+ (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) {
continue;
}
@@ -2548,12 +2712,12 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb,
(MAX3(G0[0], G1[0], G2[0]) < MIN3(RG0[0], RG1[0], RG2[0])) ||
(MIN3(G0[1], G1[1], G2[1]) > MAX3(RG0[1], RG1[1], RG2[1])) ||
(MAX3(G0[1], G1[1], G2[1]) < MIN3(RG0[1], RG1[1], RG2[1])) ||
- lineart_triangle_share_edge(rt, testing_triangle)) {
+ lineart_triangle_share_edge(tri, testing_triangle)) {
continue;
}
/* If we do need to compute intersection, then finally do it. */
- lineart_triangle_intersect(rb, rt, testing_triangle);
+ lineart_triangle_intersect(rb, tri, testing_triangle);
}
}
@@ -2585,22 +2749,11 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb)
return;
}
- rb->contour_count = 0;
- rb->contour_managed = NULL;
- rb->intersection_count = 0;
- rb->intersection_managed = NULL;
- rb->material_line_count = 0;
- rb->material_managed = NULL;
- rb->crease_count = 0;
- rb->crease_managed = NULL;
- rb->edge_mark_count = 0;
- rb->edge_mark_managed = NULL;
-
- rb->contours = NULL;
- rb->intersection_lines = NULL;
- rb->crease_lines = NULL;
- rb->material_lines = NULL;
- rb->edge_marks = NULL;
+ memset(&rb->contour, 0, sizeof(ListBase));
+ memset(&rb->crease, 0, sizeof(ListBase));
+ memset(&rb->intersection, 0, sizeof(ListBase));
+ memset(&rb->edge_mark, 0, sizeof(ListBase));
+ memset(&rb->material, 0, sizeof(ListBase));
BLI_listbase_clear(&rb->chains);
BLI_listbase_clear(&rb->wasted_cuts);
@@ -2658,9 +2811,17 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->w = scene->r.xsch;
rb->h = scene->r.ysch;
+ if (rb->cam_is_persp) {
+ rb->tile_recursive_level = LRT_TILE_RECURSIVE_PERSPECTIVE;
+ }
+ else {
+ rb->tile_recursive_level = LRT_TILE_RECURSIVE_ORTHO;
+ }
+
double asp = ((double)rb->w / (double)rb->h);
- rb->shift_x = (asp >= 1) ? c->shiftx : c->shiftx * asp;
- rb->shift_y = (asp <= 1) ? c->shifty : c->shifty * asp;
+ int fit = BKE_camera_sensor_fit(c->sensor_fit, rb->w, rb->h);
+ rb->shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp;
+ rb->shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp;
rb->crease_threshold = cos(M_PI - lmd->crease_threshold);
rb->angle_splitting_threshold = lmd->angle_splitting_threshold;
@@ -2716,7 +2877,7 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb)
rb->height_per_tile = span_h;
rb->bounding_area_count = sp_w * sp_h;
- rb->initial_bounding_areas = lineart_mem_aquire(
+ rb->initial_bounding_areas = lineart_mem_acquire(
&rb->render_data_pool, sizeof(LineartBoundingArea) * rb->bounding_area_count);
/* Initialize tiles. */
@@ -2733,6 +2894,14 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb)
ba->cx = (ba->l + ba->r) / 2;
ba->cy = (ba->u + ba->b) / 2;
+ /* Init linked_triangles array. */
+ ba->max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT;
+ ba->max_line_count = LRT_TILE_EDGE_COUNT_INITIAL;
+ ba->linked_triangles = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count);
+ ba->linked_lines = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartEdge *) * ba->max_line_count);
+
/* Link adjacent ones. */
if (row) {
lineart_list_append_pointer_pool(
@@ -2910,9 +3079,9 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb,
LineartBoundingArea *root,
int recursive_level)
{
- LineartBoundingArea *ba = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartBoundingArea) * 4);
- LineartTriangle *rt;
+ LineartBoundingArea *ba = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartBoundingArea) * 4);
+ LineartTriangle *tri;
LineartEdge *e;
ba[0].l = root->cx;
@@ -2947,35 +3116,47 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb,
lineart_bounding_areas_connect_new(rb, root);
- while ((rt = lineart_list_pop_pointer_no_free(&root->linked_triangles)) != NULL) {
+ /* Init linked_triangles array. */
+ for (int i = 0; i < 4; i++) {
+ ba[i].max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT;
+ ba[i].max_line_count = LRT_TILE_EDGE_COUNT_INITIAL;
+ ba[i].linked_triangles = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartTriangle *) * LRT_TILE_SPLITTING_TRIANGLE_LIMIT);
+ ba[i].linked_lines = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartEdge *) * LRT_TILE_EDGE_COUNT_INITIAL);
+ }
+
+ for (int i = 0; i < root->triangle_count; i++) {
+ tri = root->linked_triangles[i];
LineartBoundingArea *cba = root->child;
double b[4];
- b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
- b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
+ b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
if (LRT_BOUND_AREA_CROSSES(b, &cba[0].l)) {
- lineart_bounding_area_link_triangle(rb, &cba[0], rt, b, 0, recursive_level + 1, false);
+ lineart_bounding_area_link_triangle(rb, &cba[0], tri, b, 0, recursive_level + 1, false);
}
if (LRT_BOUND_AREA_CROSSES(b, &cba[1].l)) {
- lineart_bounding_area_link_triangle(rb, &cba[1], rt, b, 0, recursive_level + 1, false);
+ lineart_bounding_area_link_triangle(rb, &cba[1], tri, b, 0, recursive_level + 1, false);
}
if (LRT_BOUND_AREA_CROSSES(b, &cba[2].l)) {
- lineart_bounding_area_link_triangle(rb, &cba[2], rt, b, 0, recursive_level + 1, false);
+ lineart_bounding_area_link_triangle(rb, &cba[2], tri, b, 0, recursive_level + 1, false);
}
if (LRT_BOUND_AREA_CROSSES(b, &cba[3].l)) {
- lineart_bounding_area_link_triangle(rb, &cba[3], rt, b, 0, recursive_level + 1, false);
+ lineart_bounding_area_link_triangle(rb, &cba[3], tri, b, 0, recursive_level + 1, false);
}
}
- while ((e = lineart_list_pop_pointer_no_free(&root->linked_lines)) != NULL) {
- lineart_bounding_area_link_line(rb, root, e);
+ for (int i = 0; i < root->line_count; i++) {
+ e = root->linked_lines[i];
+ lineart_bounding_area_link_edge(rb, root, e);
}
rb->bounding_area_count += 3;
}
-static bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb),
+static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb),
const double l[2],
const double r[2],
LineartBoundingArea *ba)
@@ -3019,11 +3200,11 @@ static bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb)
}
static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
LineartBoundingArea *ba)
{
double p1[2], p2[2], p3[2], p4[2];
- double *FBC1 = rt->v[0]->fbcoord, *FBC2 = rt->v[1]->fbcoord, *FBC3 = rt->v[2]->fbcoord;
+ double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord;
p3[0] = p1[0] = (double)ba->l;
p2[1] = p1[1] = (double)ba->b;
@@ -3043,9 +3224,9 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb,
return true;
}
- if ((lineart_bounding_area_line_intersect(fb, FBC1, FBC2, ba)) ||
- (lineart_bounding_area_line_intersect(fb, FBC2, FBC3, ba)) ||
- (lineart_bounding_area_line_intersect(fb, FBC3, FBC1, ba))) {
+ if ((lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba)) ||
+ (lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba)) ||
+ (lineart_bounding_area_edge_intersect(fb, FBC3, FBC1, ba))) {
return true;
}
@@ -3058,27 +3239,27 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb,
*/
static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
LineartBoundingArea *root_ba,
- LineartTriangle *rt,
+ LineartTriangle *tri,
double *LRUB,
int recursive,
int recursive_level,
bool do_intersection)
{
- if (!lineart_bounding_area_triangle_intersect(rb, rt, root_ba)) {
+ if (!lineart_bounding_area_triangle_intersect(rb, tri, root_ba)) {
return;
}
if (root_ba->child == NULL) {
- lineart_list_append_pointer_pool(&root_ba->linked_triangles, &rb->render_data_pool, rt);
- root_ba->triangle_count++;
+ lineart_bounding_area_triangle_add(rb, root_ba, tri);
/* If splitting doesn't improve triangle separation, then shouldn't allow splitting anymore.
* Here we use recursive limit. This is especially useful in orthographic render,
* where a lot of faces could easily line up perfectly in image space,
* which can not be separated by simply slicing the image tile. */
- if (root_ba->triangle_count > 200 && recursive && recursive_level < 10) {
+ if (root_ba->triangle_count >= LRT_TILE_SPLITTING_TRIANGLE_LIMIT && recursive &&
+ recursive_level < rb->tile_recursive_level) {
lineart_bounding_area_split(rb, root_ba, recursive_level);
}
if (recursive && do_intersection && rb->use_intersections) {
- lineart_triangle_intersect_in_bounding_area(rb, rt, root_ba);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, root_ba);
}
}
else {
@@ -3086,54 +3267,54 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
double *B1 = LRUB;
double b[4];
if (!LRUB) {
- b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
- b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
+ b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
B1 = b;
}
if (LRT_BOUND_AREA_CROSSES(B1, &ba[0].l)) {
lineart_bounding_area_link_triangle(
- rb, &ba[0], rt, B1, recursive, recursive_level + 1, do_intersection);
+ rb, &ba[0], tri, B1, recursive, recursive_level + 1, do_intersection);
}
if (LRT_BOUND_AREA_CROSSES(B1, &ba[1].l)) {
lineart_bounding_area_link_triangle(
- rb, &ba[1], rt, B1, recursive, recursive_level + 1, do_intersection);
+ rb, &ba[1], tri, B1, recursive, recursive_level + 1, do_intersection);
}
if (LRT_BOUND_AREA_CROSSES(B1, &ba[2].l)) {
lineart_bounding_area_link_triangle(
- rb, &ba[2], rt, B1, recursive, recursive_level + 1, do_intersection);
+ rb, &ba[2], tri, B1, recursive, recursive_level + 1, do_intersection);
}
if (LRT_BOUND_AREA_CROSSES(B1, &ba[3].l)) {
lineart_bounding_area_link_triangle(
- rb, &ba[3], rt, B1, recursive, recursive_level + 1, do_intersection);
+ rb, &ba[3], tri, B1, recursive, recursive_level + 1, do_intersection);
}
}
}
-static void lineart_bounding_area_link_line(LineartRenderBuffer *rb,
+static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb,
LineartBoundingArea *root_ba,
LineartEdge *e)
{
if (root_ba->child == NULL) {
- lineart_list_append_pointer_pool(&root_ba->linked_lines, &rb->render_data_pool, e);
+ lineart_bounding_area_line_add(rb, root_ba, e);
}
else {
- if (lineart_bounding_area_line_intersect(
+ if (lineart_bounding_area_edge_intersect(
rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0])) {
- lineart_bounding_area_link_line(rb, &root_ba->child[0], e);
+ lineart_bounding_area_link_edge(rb, &root_ba->child[0], e);
}
- if (lineart_bounding_area_line_intersect(
+ if (lineart_bounding_area_edge_intersect(
rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1])) {
- lineart_bounding_area_link_line(rb, &root_ba->child[1], e);
+ lineart_bounding_area_link_edge(rb, &root_ba->child[1], e);
}
- if (lineart_bounding_area_line_intersect(
+ if (lineart_bounding_area_edge_intersect(
rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2])) {
- lineart_bounding_area_link_line(rb, &root_ba->child[2], e);
+ lineart_bounding_area_link_edge(rb, &root_ba->child[2], e);
}
- if (lineart_bounding_area_line_intersect(
+ if (lineart_bounding_area_edge_intersect(
rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3])) {
- lineart_bounding_area_link_line(rb, &root_ba->child[3], e);
+ lineart_bounding_area_link_edge(rb, &root_ba->child[3], e);
}
}
}
@@ -3149,7 +3330,7 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb)
if (lineart_get_edge_bounding_areas(rb, e, &r1, &r2, &c1, &c2)) {
for (row = r1; row != r2 + 1; row++) {
for (col = c1; col != c2 + 1; col++) {
- lineart_bounding_area_link_line(
+ lineart_bounding_area_link_edge(
rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], e);
}
}
@@ -3159,7 +3340,7 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb)
}
static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
int *rowbegin,
int *rowend,
int *colbegin,
@@ -3168,14 +3349,14 @@ static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb,
double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
double b[4];
- if (!rt->v[0] || !rt->v[1] || !rt->v[2]) {
+ if (!tri->v[0] || !tri->v[1] || !tri->v[2]) {
return false;
}
- b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[2] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
- b[3] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[2] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
+ b[3] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
return false;
@@ -3342,33 +3523,33 @@ LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, doub
*/
static void lineart_main_add_triangles(LineartRenderBuffer *rb)
{
- LineartTriangle *rt;
+ LineartTriangle *tri;
int i, lim;
int x1, x2, y1, y2;
int r, co;
- LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
- rt = reln->pointer;
- lim = reln->element_count;
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ tri = eln->pointer;
+ lim = eln->element_count;
for (i = 0; i < lim; i++) {
- if ((rt->flags & LRT_CULL_USED) || (rt->flags & LRT_CULL_DISCARD)) {
- rt = (void *)(((uchar *)rt) + rb->triangle_size);
+ if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) {
+ tri = (void *)(((uchar *)tri) + rb->triangle_size);
continue;
}
- if (lineart_get_triangle_bounding_areas(rb, rt, &y1, &y2, &x1, &x2)) {
+ if (lineart_get_triangle_bounding_areas(rb, tri, &y1, &y2, &x1, &x2)) {
for (co = x1; co <= x2; co++) {
for (r = y1; r <= y2; r++) {
lineart_bounding_area_link_triangle(rb,
&rb->initial_bounding_areas[r * LRT_BA_ROWS + co],
- rt,
+ tri,
0,
1,
0,
- (!(rt->flags & LRT_TRIANGLE_NO_INTERSECTION)));
+ (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION)));
}
}
} /* Else throw away. */
- rt = (void *)(((uchar *)rt) + rb->triangle_size);
+ tri = (void *)(((uchar *)tri) + rb->triangle_size);
}
}
}
@@ -3643,6 +3824,14 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif
Scene *scene = DEG_get_evaluated_scene(depsgraph);
int intersections_only = 0; /* Not used right now, but preserve for future. */
+ double t_start;
+
+ if (G.debug_value == 4000) {
+ t_start = PIL_check_seconds_timer();
+ }
+
+ BKE_scene_camera_switch_update(scene);
+
if (!scene->camera) {
return false;
}
@@ -3736,6 +3925,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif
if (G.debug_value == 4000) {
lineart_count_and_print_render_buffer_memory(rb);
+
+ double t_elapsed = PIL_check_seconds_timer() - t_start;
+ printf("Line art total time: %lf\n", t_elapsed);
}
return true;
@@ -3768,7 +3960,6 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
uchar transparency_mask,
short thickness,
float opacity,
- float resample_length,
const char *source_vgname,
const char *vgname,
int modifier_flags)
@@ -3802,54 +3993,53 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
int enabled_types = lineart_rb_edge_types(rb);
bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP;
bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP;
- bool preserve_weight = modifier_flags & LRT_GPENCIL_SOFT_SELECTION;
- LISTBASE_FOREACH (LineartLineChain *, rlc, &rb->chains) {
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
- if (rlc->picked) {
+ if (ec->picked) {
continue;
}
- if (!(rlc->type & (types & enabled_types))) {
+ if (!(ec->type & (types & enabled_types))) {
continue;
}
- if (rlc->level > level_end || rlc->level < level_start) {
+ if (ec->level > level_end || ec->level < level_start) {
continue;
}
- if (orig_ob && orig_ob != rlc->object_ref) {
+ if (orig_ob && orig_ob != ec->object_ref) {
continue;
}
- if (orig_col && rlc->object_ref) {
- if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)rlc->object_ref)) {
+ if (orig_col && ec->object_ref) {
+ if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)ec->object_ref)) {
continue;
}
}
if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_ENABLE) {
if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_MATCH) {
- if (rlc->transparency_mask != transparency_mask) {
+ if (ec->transparency_mask != transparency_mask) {
continue;
}
}
else {
- if (!(rlc->transparency_mask & transparency_mask)) {
+ if (!(ec->transparency_mask & transparency_mask)) {
continue;
}
}
}
/* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */
- // rlc->picked = 1;
+ // ec->picked = 1;
int array_idx = 0;
- int count = MOD_lineart_chain_count(rlc);
+ int count = MOD_lineart_chain_count(ec);
bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false);
float *stroke_data = MEM_callocN(sizeof(float) * count * GP_PRIM_DATABUF_SIZE,
"line art add stroke");
- LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) {
- stroke_data[array_idx] = rlci->gpos[0];
- stroke_data[array_idx + 1] = rlci->gpos[1];
- stroke_data[array_idx + 2] = rlci->gpos[2];
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ stroke_data[array_idx] = eci->gpos[0];
+ stroke_data[array_idx + 1] = eci->gpos[1];
+ stroke_data[array_idx + 2] = eci->gpos[2];
mul_m4_v3(gp_obmat_inverse, &stroke_data[array_idx]);
stroke_data[array_idx + 3] = 1; /* thickness. */
stroke_data[array_idx + 4] = opacity; /* hardness?. */
@@ -3858,12 +4048,12 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
BKE_gpencil_stroke_add_points(gps, stroke_data, count, mat);
BKE_gpencil_dvert_ensure(gps);
- gps->mat_nr = material_nr;
+ gps->mat_nr = max_ii(material_nr, 0);
MEM_freeN(stroke_data);
if (source_vgname && vgname) {
- Object *eval_ob = DEG_get_evaluated_object(depsgraph, rlc->object_ref);
+ Object *eval_ob = DEG_get_evaluated_object(depsgraph, ec->object_ref);
int gpdg = -1;
if ((match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0)) {
if (eval_ob && eval_ob->type == OB_MESH) {
@@ -3879,25 +4069,20 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
}
}
int sindex = 0, vindex;
- LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) {
- vindex = rlci->index;
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ vindex = eci->index;
if (vindex >= me->totvert) {
break;
}
MDeformWeight *mdw = BKE_defvert_ensure_index(&me->dvert[vindex], dindex);
MDeformWeight *gdw = BKE_defvert_ensure_index(&gps->dvert[sindex], gpdg);
- if (preserve_weight) {
- float use_weight = mdw->weight;
- if (invert_input) {
- use_weight = 1 - use_weight;
- }
- gdw->weight = MAX2(use_weight, gdw->weight);
- }
- else {
- if (mdw->weight > 0.999f) {
- gdw->weight = 1.0f;
- }
+
+ float use_weight = mdw->weight;
+ if (invert_input) {
+ use_weight = 1 - use_weight;
}
+ gdw->weight = MAX2(use_weight, gdw->weight);
+
sindex++;
}
}
@@ -3908,9 +4093,6 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
}
}
- if (resample_length > 0.0001) {
- BKE_gpencil_stroke_sample(gpencil_object->data, gps, resample_length, false);
- }
if (G.debug_value == 4000) {
BKE_gpencil_stroke_set_random_color(gps);
}
@@ -3941,7 +4123,6 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
uchar transparency_mask,
short thickness,
float opacity,
- float resample_length,
const char *source_vgname,
const char *vgname,
int modifier_flags)
@@ -3991,7 +4172,6 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
transparency_mask,
thickness,
opacity,
- resample_length,
source_vgname,
vgname,
modifier_flags);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 2c3130b46c9..e457d4a83a0 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -43,6 +43,13 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h,
struct LineartStaticMemPool *smp,
void *data,
int size);
+void *lineart_list_append_pointer_pool_thread(ListBase *h,
+ struct LineartStaticMemPool *smp,
+ void *data);
+void *lineart_list_append_pointer_pool_sized_thread(ListBase *h,
+ LineartStaticMemPool *smp,
+ void *data,
+ int size);
void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p);
void *list_push_pointer_static_sized(ListBase *h,
struct LineartStaticMemPool *smp,
@@ -54,11 +61,11 @@ void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip);
struct LineartStaticMemPoolNode *lineart_mem_new_static_pool(struct LineartStaticMemPool *smp,
size_t size);
-void *lineart_mem_aquire(struct LineartStaticMemPool *smp, size_t size);
-void *lineart_mem_aquire_thread(struct LineartStaticMemPool *smp, size_t size);
+void *lineart_mem_acquire(struct LineartStaticMemPool *smp, size_t size);
+void *lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size);
void lineart_mem_destroy(struct LineartStaticMemPool *smp);
-void lineart_prepend_edge_direct(struct LineartEdge **first, void *node);
+void lineart_prepend_edge_direct(void **list_head, void *node);
void lineart_prepend_pool(LinkNode **first, struct LineartStaticMemPool *smp, void *link);
void lineart_matrix_ortho_44d(double (*mProjection)[4],
@@ -76,29 +83,42 @@ int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb);
void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb);
#define LRT_ITER_ALL_LINES_BEGIN \
- LineartEdge *e, *next_e, **current_list; \
- e = rb->contours; \
- for (current_list = &rb->contours; e; e = next_e) { \
+ LineartEdge *e, *next_e; \
+ void **current_head; \
+ e = rb->contour.first; \
+ if (!e) { \
+ e = rb->crease.first; \
+ } \
+ if (!e) { \
+ e = rb->material.first; \
+ } \
+ if (!e) { \
+ e = rb->edge_mark.first; \
+ } \
+ if (!e) { \
+ e = rb->intersection.first; \
+ } \
+ for (current_head = &rb->contour.first; e; e = next_e) { \
next_e = e->next;
#define LRT_ITER_ALL_LINES_NEXT \
while (!next_e) { \
- if (current_list == &rb->contours) { \
- current_list = &rb->crease_lines; \
+ if (current_head == &rb->contour.first) { \
+ current_head = &rb->crease.first; \
} \
- else if (current_list == &rb->crease_lines) { \
- current_list = &rb->material_lines; \
+ else if (current_head == &rb->crease.first) { \
+ current_head = &rb->material.first; \
} \
- else if (current_list == &rb->material_lines) { \
- current_list = &rb->edge_marks; \
+ else if (current_head == &rb->material.first) { \
+ current_head = &rb->edge_mark.first; \
} \
- else if (current_list == &rb->edge_marks) { \
- current_list = &rb->intersection_lines; \
+ else if (current_head == &rb->edge_mark.first) { \
+ current_head = &rb->intersection.first; \
} \
else { \
break; \
} \
- next_e = *current_list; \
+ next_e = *current_head; \
}
#define LRT_ITER_ALL_LINES_END \
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
index 3ec32ad501e..c023c63ebc9 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
@@ -131,7 +131,6 @@ static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int
lmd->transparency_mask,
lmd->thickness,
lmd->opacity,
- lmd->resample_length,
lmd->source_vertex_group,
lmd->vgname,
lmd->flags);
@@ -365,7 +364,8 @@ static int lineart_gpencil_bake_strokes_commom_modal(bContext *C,
static void lineart_gpencil_clear_strokes_exec_common(Object *ob)
{
- if (ob->type != OB_GPENCIL) {
+ /* TODO: move these checks to an operator poll function. */
+ if ((ob == NULL) || ob->type != OB_GPENCIL) {
return;
}
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
index 4d136fe0d0e..47cca0ecd61 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
@@ -43,7 +43,7 @@ void *lineart_list_append_pointer_pool(ListBase *h, LineartStaticMemPool *smp, v
if (h == NULL) {
return 0;
}
- lip = lineart_mem_aquire(smp, sizeof(LinkData));
+ lip = lineart_mem_acquire(smp, sizeof(LinkData));
lip->data = data;
BLI_addtail(h, lip);
return lip;
@@ -57,7 +57,32 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h,
if (h == NULL) {
return 0;
}
- lip = lineart_mem_aquire(smp, size);
+ lip = lineart_mem_acquire(smp, size);
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+void *lineart_list_append_pointer_pool_thread(ListBase *h, LineartStaticMemPool *smp, void *data)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = lineart_mem_acquire_thread(smp, sizeof(LinkData));
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+void *lineart_list_append_pointer_pool_sized_thread(ListBase *h,
+ LineartStaticMemPool *smp,
+ void *data,
+ int size)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = lineart_mem_acquire_thread(smp, size);
lip->data = data;
BLI_addtail(h, lip);
return lip;
@@ -82,17 +107,17 @@ void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip)
LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size)
{
size_t set_size = size;
- if (set_size < LRT_MEMORY_POOL_64MB) {
- set_size = LRT_MEMORY_POOL_64MB; /* Prevent too many small allocations. */
+ if (set_size < LRT_MEMORY_POOL_1MB) {
+ set_size = LRT_MEMORY_POOL_1MB; /* Prevent too many small allocations. */
}
- size_t total_size = size + sizeof(LineartStaticMemPoolNode);
+ size_t total_size = set_size + sizeof(LineartStaticMemPoolNode);
LineartStaticMemPoolNode *smpn = MEM_callocN(total_size, "mempool");
smpn->size = total_size;
smpn->used_byte = sizeof(LineartStaticMemPoolNode);
BLI_addhead(&smp->pools, smpn);
return smpn;
}
-void *lineart_mem_aquire(LineartStaticMemPool *smp, size_t size)
+void *lineart_mem_acquire(LineartStaticMemPool *smp, size_t size)
{
LineartStaticMemPoolNode *smpn = smp->pools.first;
void *ret;
@@ -107,7 +132,7 @@ void *lineart_mem_aquire(LineartStaticMemPool *smp, size_t size)
return ret;
}
-void *lineart_mem_aquire_thread(LineartStaticMemPool *smp, size_t size)
+void *lineart_mem_acquire_thread(LineartStaticMemPool *smp, size_t size)
{
void *ret;
@@ -135,16 +160,16 @@ void lineart_mem_destroy(LineartStaticMemPool *smp)
}
}
-void lineart_prepend_edge_direct(LineartEdge **first, void *node)
+void lineart_prepend_edge_direct(void **list_head, void *node)
{
LineartEdge *e_n = (LineartEdge *)node;
- e_n->next = (*first);
- (*first) = e_n;
+ e_n->next = (*list_head);
+ (*list_head) = e_n;
}
void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link)
{
- LinkNode *ln = lineart_mem_aquire_thread(smp, sizeof(LinkNode));
+ LinkNode *ln = lineart_mem_acquire_thread(smp, sizeof(LinkNode));
ln->next = (*first);
ln->link = link;
(*first) = ln;
@@ -211,7 +236,7 @@ void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb)
LISTBASE_FOREACH (LineartStaticMemPoolNode *, smpn, &rb->render_data_pool.pools) {
count_this++;
- sum_this += LRT_MEMORY_POOL_64MB;
+ sum_this += LRT_MEMORY_POOL_1MB;
}
printf("LANPR Memory allocated %zu Standalone nodes, total %zu Bytes.\n", count_this, sum_this);
total += sum_this;
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 69a79e2f2ce..8468985309f 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -62,6 +62,7 @@ set(SRC
intern/gpu_buffers.c
intern/gpu_capabilities.cc
intern/gpu_codegen.c
+ intern/gpu_compute.cc
intern/gpu_context.cc
intern/gpu_debug.cc
intern/gpu_drawlist.cc
@@ -91,6 +92,7 @@ set(SRC
opengl/gl_backend.cc
opengl/gl_batch.cc
+ opengl/gl_compute.cc
opengl/gl_context.cc
opengl/gl_debug.cc
opengl/gl_debug_layer.cc
@@ -113,6 +115,7 @@ set(SRC
GPU_buffers.h
GPU_capabilities.h
GPU_common.h
+ GPU_compute.h
GPU_context.h
GPU_debug.h
GPU_drawlist.h
@@ -163,6 +166,7 @@ set(SRC
opengl/gl_backend.hh
opengl/gl_batch.hh
+ opengl/gl_compute.hh
opengl/gl_context.hh
opengl/gl_debug.hh
opengl/gl_drawlist.hh
@@ -275,6 +279,7 @@ data_to_c_simple(shaders/material/gpu_shader_material_anisotropic.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_attribute.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_background.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bevel.glsl SRC)
+data_to_c_simple(shaders/material/gpu_shader_material_wavelength.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_blackbody.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bright_contrast.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bump.glsl SRC)
@@ -381,11 +386,18 @@ endif()
blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+if(CXX_WARN_NO_SUGGEST_OVERRIDE)
+ target_compile_options(bf_gpu PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>)
+endif()
+
if(WITH_GTESTS)
if(WITH_OPENGL_DRAW_TESTS)
set(TEST_SRC
tests/gpu_testing.cc
+ tests/gpu_index_buffer_test.cc
+ tests/gpu_shader_test.cc
+
tests/gpu_testing.hh
)
set(TEST_INC
diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h
index b95053a3715..0c054d4f264 100644
--- a/source/blender/gpu/GPU_capabilities.h
+++ b/source/blender/gpu/GPU_capabilities.h
@@ -37,6 +37,17 @@ int GPU_max_textures(void);
int GPU_max_textures_vert(void);
int GPU_max_textures_geom(void);
int GPU_max_textures_frag(void);
+int GPU_max_work_group_count(int index);
+int GPU_max_work_group_size(int index);
+int GPU_max_uniforms_vert(void);
+int GPU_max_uniforms_frag(void);
+int GPU_max_batch_indices(void);
+int GPU_max_batch_vertices(void);
+int GPU_max_vertex_attribs(void);
+int GPU_max_varying_floats(void);
+
+int GPU_extensions_len(void);
+const char *GPU_extension_get(int i);
int GPU_texture_size_with_limit(int res, bool limit_gl_texture_size);
@@ -46,6 +57,8 @@ bool GPU_use_main_context_workaround(void);
bool GPU_use_hq_normals_workaround(void);
bool GPU_crappy_amd_driver(void);
+bool GPU_compute_shader_support(void);
+bool GPU_shader_storage_buffer_objects_support(void);
bool GPU_shader_image_load_store_support(void);
bool GPU_mem_stats_supported(void);
diff --git a/source/blender/gpu/GPU_common.h b/source/blender/gpu/GPU_common.h
index 1be74701176..c00bf581e8c 100644
--- a/source/blender/gpu/GPU_common.h
+++ b/source/blender/gpu/GPU_common.h
@@ -24,6 +24,7 @@
#pragma once
#define PROGRAM_NO_OPTI 0
+//#define GPU_NO_USE_PY_REFERENCES
#if defined(NDEBUG)
# define TRUST_NO_ONE 0
diff --git a/source/blender/blenkernel/BKE_mesh_boolean_convert.h b/source/blender/gpu/GPU_compute.h
index c126d714542..a048f72c0a0 100644
--- a/source/blender/blenkernel/BKE_mesh_boolean_convert.h
+++ b/source/blender/gpu/GPU_compute.h
@@ -12,28 +12,26 @@
* 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) 2019 Blender Foundation.
- * All rights reserved.
*/
-#pragma once
-
/** \file
- * \ingroup bke
+ * \ingroup gpu
*/
+#pragma once
+
+#include "BLI_sys_types.h"
+
+#include "GPU_shader.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-Mesh *BKE_mesh_boolean(const Mesh **meshes,
- const float (*obmats[])[4][4],
- const short **material_remaps,
- const int meshes_len,
- const bool use_self,
- const bool hole_tolerant,
- const int boolean_mode);
+void GPU_compute_dispatch(GPUShader *shader,
+ uint groups_x_len,
+ uint groups_y_len,
+ uint groups_z_len);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index af94c1fb0e6..6482d9a9d3e 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -209,6 +209,11 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
void (*callback)(void *userData, int level),
void *userData);
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb);
+void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref);
+#endif
+
void GPU_framebuffer_push(GPUFrameBuffer *fb);
GPUFrameBuffer *GPU_framebuffer_pop(void);
uint GPU_framebuffer_stack_level_get(void);
diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h
index 76aab3c196b..a89ed91674f 100644
--- a/source/blender/gpu/GPU_index_buffer.h
+++ b/source/blender/gpu/GPU_index_buffer.h
@@ -40,6 +40,8 @@ typedef struct GPUIndexBufBuilder {
uint max_allowed_index;
uint max_index_len;
uint index_len;
+ uint index_min;
+ uint index_max;
GPUPrimType prim_type;
uint32_t *data;
} GPUIndexBufBuilder;
@@ -49,6 +51,14 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uin
/* supports only GPU_PRIM_POINTS, GPU_PRIM_LINES and GPU_PRIM_TRIS. */
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len);
+GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len);
+
+/*
+ * Thread safe.
+ *
+ * Function inspired by the reduction directives of multithread work APIs..
+ */
+void GPU_indexbuf_join(GPUIndexBufBuilder *builder, const GPUIndexBufBuilder *parent_builder);
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v);
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *);
@@ -70,6 +80,8 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem);
GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *);
void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, GPUIndexBuf *);
+void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding);
+
/* Create a sub-range of an existing index-buffer. */
GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length);
void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem,
@@ -77,6 +89,15 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem,
uint start,
uint length);
+/**
+ * (Download and) return a pointer containing the data of an index buffer.
+ *
+ * Note that the returned pointer is still owned by the driver. To get an
+ * local copy, use `GPU_indexbuf_unmap` after calling `GPU_indexbuf_read`.
+ */
+const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem);
+uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer);
+
void GPU_indexbuf_discard(GPUIndexBuf *elem);
bool GPU_indexbuf_is_init(GPUIndexBuf *elem);
diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h
index aad6ae9e2ba..e073263f352 100644
--- a/source/blender/gpu/GPU_matrix.h
+++ b/source/blender/gpu/GPU_matrix.h
@@ -118,21 +118,27 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *unproj_prec
const float proj[4][4],
const int view[4]);
-void GPU_matrix_project(const float world[3],
- const float model[4][4],
- const float proj[4][4],
- const int view[4],
- float r_win[3]);
-
-bool GPU_matrix_unproject(const float win[3],
- const float model[4][4],
- const float proj[4][4],
- const int view[4],
- float r_world[3]);
-
-void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc,
- const float win[3],
- float r_world[3]);
+void GPU_matrix_project_3fv(const float world[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float r_win[3]);
+
+void GPU_matrix_project_2fv(const float world[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float r_win[2]);
+
+bool GPU_matrix_unproject_3fv(const float win[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float r_world[3]);
+
+void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc,
+ const float win[3],
+ float r_world[3]);
/* 2D Projection Matrix */
diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h
index c457b829bf7..fa7d5d7fba8 100644
--- a/source/blender/gpu/GPU_platform.h
+++ b/source/blender/gpu/GPU_platform.h
@@ -68,6 +68,9 @@ extern "C" {
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver);
eGPUSupportLevel GPU_platform_support_level(void);
+const char *GPU_platform_vendor(void);
+const char *GPU_platform_renderer(void);
+const char *GPU_platform_version(void);
const char *GPU_platform_support_level_key(void);
const char *GPU_platform_gpu_name(void);
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 9824c7016dc..3923c920c9e 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -27,6 +27,7 @@
extern "C" {
#endif
+struct GPUIndexBuf;
struct GPUVertBuf;
/** Opaque type hiding #blender::gpu::Shader */
@@ -45,6 +46,10 @@ GPUShader *GPU_shader_create(const char *vertcode,
const char *libcode,
const char *defines,
const char *shname);
+GPUShader *GPU_shader_create_compute(const char *computecode,
+ const char *libcode,
+ const char *defines,
+ const char *shname);
GPUShader *GPU_shader_create_from_python(const char *vertcode,
const char *fragcode,
const char *geomcode,
@@ -53,6 +58,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode,
GPUShader *GPU_shader_create_ex(const char *vertcode,
const char *fragcode,
const char *geomcode,
+ const char *computecode,
const char *libcode,
const char *defines,
const eGPUShaderTFBType tf_type,
@@ -126,6 +132,7 @@ int GPU_shader_get_uniform(GPUShader *shader, const char *name);
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin);
int GPU_shader_get_builtin_block(GPUShader *shader, int builtin);
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name);
+int GPU_shader_get_ssbo(GPUShader *shader, const char *name);
int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name);
int GPU_shader_get_texture_binding(GPUShader *shader, const char *name);
diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h
index 0687f271670..a338728804c 100644
--- a/source/blender/gpu/GPU_state.h
+++ b/source/blender/gpu/GPU_state.h
@@ -39,6 +39,7 @@ typedef enum eGPUBarrier {
GPU_BARRIER_NONE = 0,
GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0),
GPU_BARRIER_TEXTURE_FETCH = (1 << 1),
+ GPU_BARRIER_SHADER_STORAGE = (1 << 2),
} eGPUBarrier;
ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_TEXTURE_FETCH)
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index d9a01663de0..f980c8fdcd7 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -269,6 +269,12 @@ bool GPU_texture_cube(const GPUTexture *tex);
bool GPU_texture_depth(const GPUTexture *tex);
bool GPU_texture_stencil(const GPUTexture *tex);
bool GPU_texture_integer(const GPUTexture *tex);
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_texture_py_reference_get(GPUTexture *tex);
+void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref);
+#endif
+
int GPU_texture_opengl_bindcode(const GPUTexture *tex);
void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size);
diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h
index aae58de533b..2c54016daa7 100644
--- a/source/blender/gpu/GPU_vertex_buffer.h
+++ b/source/blender/gpu/GPU_vertex_buffer.h
@@ -59,6 +59,7 @@ typedef enum {
GPU_USAGE_STREAM,
GPU_USAGE_STATIC, /* do not keep data in memory */
GPU_USAGE_DYNAMIC,
+ GPU_USAGE_DEVICE_ONLY, /* Do not do host->device data transfers. */
} GPUUsageType;
/** Opaque type hiding blender::gpu::VertBuf. */
@@ -70,6 +71,14 @@ GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *, GPUUsageTyp
#define GPU_vertbuf_create_with_format(format) \
GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_STATIC)
+/**
+ * (Download and) return a pointer containing the data of a vertex buffer.
+ *
+ * Note that the returned pointer is still owned by the driver. To get an
+ * local copy, use `GPU_vertbuf_unmap` after calling `GPU_vertbuf_read`.
+ */
+const void *GPU_vertbuf_read(GPUVertBuf *verts);
+void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data);
void GPU_vertbuf_clear(GPUVertBuf *verts);
void GPU_vertbuf_discard(GPUVertBuf *);
@@ -138,6 +147,7 @@ uint GPU_vertbuf_get_vertex_len(const GPUVertBuf *verts);
GPUVertBufStatus GPU_vertbuf_get_status(const GPUVertBuf *verts);
void GPU_vertbuf_use(GPUVertBuf *);
+void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding);
/* XXX do not use. */
void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data);
diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh
index 04ec82a9213..73792215569 100644
--- a/source/blender/gpu/intern/gpu_backend.hh
+++ b/source/blender/gpu/intern/gpu_backend.hh
@@ -47,6 +47,7 @@ class GPUBackend {
static GPUBackend *get(void);
virtual void samplers_update(void) = 0;
+ virtual void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) = 0;
virtual Context *context_alloc(void *ghost_window) = 0;
diff --git a/source/blender/gpu/intern/gpu_batch_private.hh b/source/blender/gpu/intern/gpu_batch_private.hh
index d0fbd1432b3..eed96864f20 100644
--- a/source/blender/gpu/intern/gpu_batch_private.hh
+++ b/source/blender/gpu/intern/gpu_batch_private.hh
@@ -42,8 +42,7 @@ namespace gpu {
*/
class Batch : public GPUBatch {
public:
- Batch(){};
- virtual ~Batch(){};
+ virtual ~Batch() = default;
virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0;
diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc
index 6d9182dcf17..c6e9dc210cb 100644
--- a/source/blender/gpu/intern/gpu_capabilities.cc
+++ b/source/blender/gpu/intern/gpu_capabilities.cc
@@ -82,6 +82,56 @@ int GPU_max_textures(void)
return GCaps.max_textures;
}
+int GPU_max_work_group_count(int index)
+{
+ return GCaps.max_work_group_count[index];
+}
+
+int GPU_max_work_group_size(int index)
+{
+ return GCaps.max_work_group_size[index];
+}
+
+int GPU_max_uniforms_vert(void)
+{
+ return GCaps.max_uniforms_vert;
+}
+
+int GPU_max_uniforms_frag(void)
+{
+ return GCaps.max_uniforms_frag;
+}
+
+int GPU_max_batch_indices(void)
+{
+ return GCaps.max_batch_indices;
+}
+
+int GPU_max_batch_vertices(void)
+{
+ return GCaps.max_batch_vertices;
+}
+
+int GPU_max_vertex_attribs(void)
+{
+ return GCaps.max_vertex_attribs;
+}
+
+int GPU_max_varying_floats(void)
+{
+ return GCaps.max_varying_floats;
+}
+
+int GPU_extensions_len(void)
+{
+ return GCaps.extensions_len;
+}
+
+const char *GPU_extension_get(int i)
+{
+ return GCaps.extension_get ? GCaps.extension_get(i) : "\0";
+}
+
bool GPU_mip_render_workaround(void)
{
return GCaps.mip_render_workaround;
@@ -108,6 +158,16 @@ bool GPU_use_hq_normals_workaround(void)
return GCaps.use_hq_normals_workaround;
}
+bool GPU_compute_shader_support(void)
+{
+ return GCaps.compute_shader_support;
+}
+
+bool GPU_shader_storage_buffer_objects_support(void)
+{
+ return GCaps.shader_storage_buffer_objects_support;
+}
+
bool GPU_shader_image_load_store_support(void)
{
return GCaps.shader_image_load_store_support;
diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh
index 2b3292749f8..95cf7fd335d 100644
--- a/source/blender/gpu/intern/gpu_capabilities_private.hh
+++ b/source/blender/gpu/intern/gpu_capabilities_private.hh
@@ -41,7 +41,20 @@ struct GPUCapabilities {
int max_textures_vert = 0;
int max_textures_geom = 0;
int max_textures_frag = 0;
+ int max_work_group_count[3] = {0, 0, 0};
+ int max_work_group_size[3] = {0, 0, 0};
+ int max_uniforms_vert = 0;
+ int max_uniforms_frag = 0;
+ int max_batch_indices = 0;
+ int max_batch_vertices = 0;
+ int max_vertex_attribs = 0;
+ int max_varying_floats = 0;
+ int extensions_len = 0;
+ const char *(*extension_get)(int);
+
bool mem_stats_support = false;
+ bool compute_shader_support = false;
+ bool shader_storage_buffer_objects_support = false;
bool shader_image_load_store_support = false;
/* OpenGL related workarounds. */
bool mip_render_workaround = false;
diff --git a/source/blender/gpu/intern/gpu_compute.cc b/source/blender/gpu/intern/gpu_compute.cc
new file mode 100644
index 00000000000..7a8ae2acf9a
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_compute.cc
@@ -0,0 +1,41 @@
+/*
+ * 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 gpu
+ */
+
+#include "GPU_compute.h"
+
+#include "gpu_backend.hh"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GPU_compute_dispatch(GPUShader *shader,
+ uint groups_x_len,
+ uint groups_y_len,
+ uint groups_z_len)
+{
+ blender::gpu::GPUBackend &gpu_backend = *blender::gpu::GPUBackend::get();
+ GPU_shader_bind(shader);
+ gpu_backend.compute_dispatch(groups_x_len, groups_y_len, groups_z_len);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc
index a9d32dcf297..b5a437b46f7 100644
--- a/source/blender/gpu/intern/gpu_context.cc
+++ b/source/blender/gpu/intern/gpu_context.cc
@@ -24,7 +24,7 @@
* Use these instead of glGenBuffers & its friends
* - alloc must be called from a thread that is bound
* to the context that will be used for drawing with
- * this vao.
+ * this VAO.
* - free can be called from any thread
*/
diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc
index 63e7024b74b..2d9fb7822ae 100644
--- a/source/blender/gpu/intern/gpu_debug.cc
+++ b/source/blender/gpu/intern/gpu_debug.cc
@@ -86,8 +86,8 @@ bool GPU_debug_group_match(const char *ref)
if (ctx == nullptr) {
return false;
}
- DebugStack &stack = ctx->debug_stack;
- for (StringRef &name : stack) {
+ const DebugStack &stack = ctx->debug_stack;
+ for (const StringRef &name : stack) {
if (name == ref) {
return true;
}
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index 1e3cf479462..1293cc0953d 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -71,6 +71,12 @@ FrameBuffer::~FrameBuffer()
reinterpret_cast<Texture *>(attachment.tex)->detach_from(this);
}
}
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (this->py_ref) {
+ *this->py_ref = nullptr;
+ }
+#endif
}
/** \} */
@@ -473,6 +479,19 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb,
unwrap(gpu_fb)->recursive_downsample(max_lvl, callback, userData);
}
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb)
+{
+ return unwrap(gpu_fb)->py_ref;
+}
+
+void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref)
+{
+ BLI_assert(py_ref == nullptr || unwrap(gpu_fb)->py_ref == nullptr);
+ unwrap(gpu_fb)->py_ref = py_ref;
+}
+#endif
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh
index d63d72cf4f7..2b00c239af4 100644
--- a/source/blender/gpu/intern/gpu_framebuffer_private.hh
+++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh
@@ -100,6 +100,20 @@ class FrameBuffer {
bool scissor_test_ = false;
bool dirty_state_ = true;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ public:
+ /**
+ * Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
+ * Points to #BPyGPUFrameBuffer.fb
+ */
+ void **py_ref = nullptr;
+#endif
+
+ public:
+ /* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
+ * Points to #BPyGPUFrameBuffer::fb */
+ void **ref = nullptr;
+
public:
FrameBuffer(const char *name);
virtual ~FrameBuffer();
diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc
index 65932d2dbf4..be7470e79c1 100644
--- a/source/blender/gpu/intern/gpu_index_buffer.cc
+++ b/source/blender/gpu/intern/gpu_index_buffer.cc
@@ -25,12 +25,15 @@
#include "MEM_guardedalloc.h"
+#include "BLI_math_base.h"
#include "BLI_utildefines.h"
#include "gpu_backend.hh"
#include "gpu_index_buffer_private.hh"
+#include <cstring>
+
#define KEEP_SINGLE_COPY 1
#define RESTART_INDEX 0xFFFFFFFF
@@ -50,6 +53,8 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder,
builder->max_allowed_index = vertex_len - 1;
builder->max_index_len = index_len;
builder->index_len = 0; // start empty
+ builder->index_min = UINT32_MAX;
+ builder->index_max = 0;
builder->prim_type = prim_type;
builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data");
}
@@ -66,6 +71,22 @@ void GPU_indexbuf_init(GPUIndexBufBuilder *builder,
GPU_indexbuf_init_ex(builder, prim_type, prim_len * (uint)verts_per_prim, vertex_len);
}
+GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len)
+{
+ GPUIndexBuf *elem_ = GPU_indexbuf_calloc();
+ IndexBuf *elem = unwrap(elem_);
+ elem->init_build_on_device(index_len);
+ return elem_;
+}
+
+void GPU_indexbuf_join(GPUIndexBufBuilder *builder_to, const GPUIndexBufBuilder *builder_from)
+{
+ BLI_assert(builder_to->data == builder_from->data);
+ builder_to->index_len = max_uu(builder_to->index_len, builder_from->index_len);
+ builder_to->index_min = min_uu(builder_to->index_min, builder_from->index_min);
+ builder_to->index_max = max_uu(builder_to->index_max, builder_from->index_max);
+}
+
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v)
{
#if TRUST_NO_ONE
@@ -74,6 +95,8 @@ void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v)
assert(v <= builder->max_allowed_index);
#endif
builder->data[builder->index_len++] = v;
+ builder->index_min = MIN2(builder->index_min, v);
+ builder->index_max = MAX2(builder->index_max, v);
}
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *builder)
@@ -132,9 +155,9 @@ void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
BLI_assert(elem < builder->max_index_len);
builder->data[elem++] = v1;
- if (builder->index_len < elem) {
- builder->index_len = elem;
- }
+ builder->index_min = MIN2(builder->index_min, v1);
+ builder->index_max = MAX2(builder->index_max, v1);
+ builder->index_len = MAX2(builder->index_len, elem);
}
void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2)
@@ -147,9 +170,9 @@ void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1
uint idx = elem * 2;
builder->data[idx++] = v1;
builder->data[idx++] = v2;
- if (builder->index_len < idx) {
- builder->index_len = idx;
- }
+ builder->index_min = MIN3(builder->index_min, v1, v2);
+ builder->index_max = MAX3(builder->index_max, v1, v2);
+ builder->index_len = MAX2(builder->index_len, idx);
}
void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3)
@@ -164,9 +187,10 @@ void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1,
builder->data[idx++] = v1;
builder->data[idx++] = v2;
builder->data[idx++] = v3;
- if (builder->index_len < idx) {
- builder->index_len = idx;
- }
+
+ builder->index_min = MIN4(builder->index_min, v1, v2, v3);
+ builder->index_max = MAX4(builder->index_max, v1, v2, v3);
+ builder->index_len = MAX2(builder->index_len, idx);
}
void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem)
@@ -174,9 +198,7 @@ void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem)
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
BLI_assert(elem < builder->max_index_len);
builder->data[elem++] = RESTART_INDEX;
- if (builder->index_len < elem) {
- builder->index_len = elem;
- }
+ builder->index_len = MAX2(builder->index_len, elem);
}
void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem)
@@ -186,9 +208,7 @@ void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem)
uint idx = elem * 2;
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
- if (builder->index_len < idx) {
- builder->index_len = idx;
- }
+ builder->index_len = MAX2(builder->index_len, idx);
}
void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
@@ -199,9 +219,7 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
- if (builder->index_len < idx) {
- builder->index_len = idx;
- }
+ builder->index_len = MAX2(builder->index_len, idx);
}
/** \} */
@@ -219,7 +237,7 @@ IndexBuf::~IndexBuf()
}
}
-void IndexBuf::init(uint indices_len, uint32_t *indices)
+void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint max_index)
{
is_init_ = true;
data_ = indices;
@@ -229,8 +247,7 @@ void IndexBuf::init(uint indices_len, uint32_t *indices)
#if GPU_TRACK_INDEX_RANGE
/* Everything remains 32 bit while building to keep things simple.
* Find min/max after, then convert to smallest index type possible. */
- uint min_index, max_index;
- uint range = this->index_range(&min_index, &max_index);
+ uint range = min_index < max_index ? max_index - min_index : 0;
/* count the primitive restart index. */
range += 1;
@@ -241,6 +258,15 @@ void IndexBuf::init(uint indices_len, uint32_t *indices)
#endif
}
+void IndexBuf::init_build_on_device(uint index_len)
+{
+ is_init_ = true;
+ index_start_ = 0;
+ index_len_ = index_len;
+ index_type_ = GPU_INDEX_U32;
+ data_ = nullptr;
+}
+
void IndexBuf::init_subrange(IndexBuf *elem_src, uint start, uint length)
{
/* We don't support nested subranges. */
@@ -307,6 +333,14 @@ void IndexBuf::squeeze_indices_short(uint min_idx, uint max_idx)
}
}
+uint32_t *IndexBuf::unmap(const uint32_t *mapped_memory) const
+{
+ size_t size = size_get();
+ uint32_t *result = static_cast<uint32_t *>(MEM_mallocN(size, __func__));
+ memcpy(result, mapped_memory, size);
+ return result;
+}
+
} // namespace blender::gpu
/** \} */
@@ -339,7 +373,7 @@ void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *builder, GPUIndexBuf *elem)
BLI_assert(builder->data != nullptr);
/* Transfer data ownership to GPUIndexBuf.
* It will be uploaded upon first use. */
- unwrap(elem)->init(builder->index_len, builder->data);
+ unwrap(elem)->init(builder->index_len, builder->data, builder->index_min, builder->index_max);
builder->data = nullptr;
}
@@ -351,6 +385,16 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem,
unwrap(elem)->init_subrange(unwrap(elem_src), start, length);
}
+const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem)
+{
+ return unwrap(elem)->read();
+}
+
+uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer)
+{
+ return unwrap(elem)->unmap(mapped_buffer);
+}
+
void GPU_indexbuf_discard(GPUIndexBuf *elem)
{
delete unwrap(elem);
@@ -366,4 +410,9 @@ int GPU_indexbuf_primitive_len(GPUPrimType prim_type)
return indices_per_primitive(prim_type);
}
+void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding)
+{
+ unwrap(elem)->bind_as_ssbo(binding);
+}
+
/** \} */
diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh
index 2405db8664a..e8a47417604 100644
--- a/source/blender/gpu/intern/gpu_index_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh
@@ -73,15 +73,16 @@ class IndexBuf {
IndexBuf(){};
virtual ~IndexBuf();
- void init(uint indices_len, uint32_t *indices);
+ void init(uint indices_len, uint32_t *indices, uint min_index, uint max_index);
void init_subrange(IndexBuf *elem_src, uint start, uint length);
+ void init_build_on_device(uint index_len);
uint32_t index_len_get(void) const
{
return index_len_;
}
/* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */
- size_t size_get(void)
+ size_t size_get(void) const
{
return index_len_ * to_bytesize(index_type_);
};
@@ -91,6 +92,11 @@ class IndexBuf {
return is_init_;
};
+ virtual void bind_as_ssbo(uint binding) = 0;
+
+ virtual const uint32_t *read() const = 0;
+ uint32_t *unmap(const uint32_t *mapped_memory) const;
+
private:
inline void squeeze_indices_short(uint min_idx, uint max_idx);
inline uint index_range(uint *r_min, uint *r_max);
@@ -105,6 +111,10 @@ static inline IndexBuf *unwrap(GPUIndexBuf *indexbuf)
{
return reinterpret_cast<IndexBuf *>(indexbuf);
}
+static inline const IndexBuf *unwrap(const GPUIndexBuf *indexbuf)
+{
+ return reinterpret_cast<const IndexBuf *>(indexbuf);
+}
static inline int indices_per_primitive(GPUPrimType prim_type)
{
diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c
index 175facc0a8d..3c216c1a991 100644
--- a/source/blender/gpu/intern/gpu_material_library.c
+++ b/source/blender/gpu/intern/gpu_material_library.c
@@ -47,6 +47,7 @@ extern char datatoc_gpu_shader_material_anisotropic_glsl[];
extern char datatoc_gpu_shader_material_attribute_glsl[];
extern char datatoc_gpu_shader_material_background_glsl[];
extern char datatoc_gpu_shader_material_bevel_glsl[];
+extern char datatoc_gpu_shader_material_wavelength_glsl[];
extern char datatoc_gpu_shader_material_blackbody_glsl[];
extern char datatoc_gpu_shader_material_bright_contrast_glsl[];
extern char datatoc_gpu_shader_material_bump_glsl[];
@@ -191,6 +192,11 @@ static GPUMaterialLibrary gpu_shader_material_bevel_library = {
.dependencies = {NULL},
};
+static GPUMaterialLibrary gpu_shader_material_wavelength_library = {
+ .code = datatoc_gpu_shader_material_wavelength_glsl,
+ .dependencies = {NULL},
+};
+
static GPUMaterialLibrary gpu_shader_material_blackbody_library = {
.code = datatoc_gpu_shader_material_blackbody_glsl,
.dependencies = {NULL},
@@ -593,6 +599,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = {
&gpu_shader_material_attribute_library,
&gpu_shader_material_background_library,
&gpu_shader_material_bevel_library,
+ &gpu_shader_material_wavelength_library,
&gpu_shader_material_blackbody_library,
&gpu_shader_material_bright_contrast_library,
&gpu_shader_material_bump_library,
diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc
index 2ae50d913da..6eb9cb823d5 100644
--- a/source/blender/gpu/intern/gpu_matrix.cc
+++ b/source/blender/gpu/intern/gpu_matrix.cc
@@ -474,11 +474,11 @@ void GPU_matrix_look_at(float eyeX,
GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ);
}
-void GPU_matrix_project(const float world[3],
- const float model[4][4],
- const float proj[4][4],
- const int view[4],
- float win[3])
+void GPU_matrix_project_3fv(const float world[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float win[3])
{
float v[4];
@@ -494,6 +494,25 @@ void GPU_matrix_project(const float world[3],
win[2] = (v[2] + 1) * 0.5f;
}
+void GPU_matrix_project_2fv(const float world[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float win[2])
+{
+ float v[4];
+
+ mul_v4_m4v3(v, model, world);
+ mul_m4_v4(proj, v);
+
+ if (v[3] != 0.0f) {
+ mul_v2_fl(v, 1.0f / v[3]);
+ }
+
+ win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f;
+ win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f;
+}
+
/**
* The same result could be obtained as follows:
*
@@ -543,7 +562,7 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
&precalc->dims.zmax);
if (isinf(precalc->dims.zmax)) {
/* We cannot retrieve the actual value of the clip_end.
- * Use `FLT_MAX` to avoid nans. */
+ * Use `FLT_MAX` to avoid NAN's. */
precalc->dims.zmax = FLT_MAX;
}
for (int i = 0; i < 4; i++) {
@@ -556,9 +575,9 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
return true;
}
-void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc,
- const float win[3],
- float r_world[3])
+void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc,
+ const float win[3],
+ float r_world[3])
{
float in[3] = {
(win[0] - precalc->view[0]) / precalc->view[2],
@@ -569,18 +588,18 @@ void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *
mul_v3_m4v3(r_world, precalc->model_inverted, in);
}
-bool GPU_matrix_unproject(const float win[3],
- const float model[4][4],
- const float proj[4][4],
- const int view[4],
- float r_world[3])
+bool GPU_matrix_unproject_3fv(const float win[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float r_world[3])
{
struct GPUMatrixUnproject_Precalc precalc;
if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) {
zero_v3(r_world);
return false;
}
- GPU_matrix_unproject_with_precalc(&precalc, win, r_world);
+ GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world);
return true;
}
diff --git a/source/blender/gpu/intern/gpu_platform.cc b/source/blender/gpu/intern/gpu_platform.cc
index 6b9878f2ba4..49dde473300 100644
--- a/source/blender/gpu/intern/gpu_platform.cc
+++ b/source/blender/gpu/intern/gpu_platform.cc
@@ -41,10 +41,10 @@ namespace blender::gpu {
GPUPlatformGlobal GPG;
-void GPUPlatformGlobal::create_key(eGPUSupportLevel support_level,
- const char *vendor,
- const char *renderer,
- const char *version)
+static char *create_key(eGPUSupportLevel support_level,
+ const char *vendor,
+ const char *renderer,
+ const char *version)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_appendf(ds, "{%s/%s/%s}=", vendor, renderer, version);
@@ -58,29 +58,56 @@ void GPUPlatformGlobal::create_key(eGPUSupportLevel support_level,
BLI_dynstr_append(ds, "UNSUPPORTED");
}
- support_key = BLI_dynstr_get_cstring(ds);
+ char *support_key = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
BLI_str_replace_char(support_key, '\n', ' ');
BLI_str_replace_char(support_key, '\r', ' ');
+ return support_key;
}
-void GPUPlatformGlobal::create_gpu_name(const char *vendor,
- const char *renderer,
- const char *version)
+static char *create_gpu_name(const char *vendor, const char *renderer, const char *version)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_appendf(ds, "%s %s %s", vendor, renderer, version);
- gpu_name = BLI_dynstr_get_cstring(ds);
+ char *gpu_name = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
BLI_str_replace_char(gpu_name, '\n', ' ');
BLI_str_replace_char(gpu_name, '\r', ' ');
+ return gpu_name;
+}
+
+void GPUPlatformGlobal::init(eGPUDeviceType gpu_device,
+ eGPUOSType os_type,
+ eGPUDriverType driver_type,
+ eGPUSupportLevel gpu_support_level,
+ const char *vendor_str,
+ const char *renderer_str,
+ const char *version_str)
+{
+ this->clear();
+
+ this->initialized = true;
+
+ this->device = gpu_device;
+ this->os = os_type;
+ this->driver = driver_type;
+ this->support_level = gpu_support_level;
+
+ this->vendor = BLI_strdup(vendor_str);
+ this->renderer = BLI_strdup(renderer_str);
+ this->version = BLI_strdup(version_str);
+ this->support_key = create_key(gpu_support_level, vendor_str, renderer_str, version_str);
+ this->gpu_name = create_gpu_name(vendor_str, renderer_str, version_str);
}
void GPUPlatformGlobal::clear()
{
- MEM_SAFE_FREE(GPG.support_key);
- MEM_SAFE_FREE(GPG.gpu_name);
+ MEM_SAFE_FREE(vendor);
+ MEM_SAFE_FREE(renderer);
+ MEM_SAFE_FREE(version);
+ MEM_SAFE_FREE(support_key);
+ MEM_SAFE_FREE(gpu_name);
initialized = false;
}
@@ -96,22 +123,44 @@ using namespace blender::gpu;
eGPUSupportLevel GPU_platform_support_level()
{
+ BLI_assert(GPG.initialized);
return GPG.support_level;
}
-const char *GPU_platform_support_level_key()
+const char *GPU_platform_vendor(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.vendor;
+}
+
+const char *GPU_platform_renderer(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.renderer;
+}
+
+const char *GPU_platform_version(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.version;
+}
+
+const char *GPU_platform_support_level_key(void)
{
+ BLI_assert(GPG.initialized);
return GPG.support_key;
}
const char *GPU_platform_gpu_name(void)
{
+ BLI_assert(GPG.initialized);
return GPG.gpu_name;
}
/* GPU Types */
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
{
+ BLI_assert(GPG.initialized);
return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver);
}
diff --git a/source/blender/gpu/intern/gpu_platform_private.hh b/source/blender/gpu/intern/gpu_platform_private.hh
index 02d99efa4a9..f823269ab54 100644
--- a/source/blender/gpu/intern/gpu_platform_private.hh
+++ b/source/blender/gpu/intern/gpu_platform_private.hh
@@ -34,16 +34,20 @@ class GPUPlatformGlobal {
eGPUOSType os;
eGPUDriverType driver;
eGPUSupportLevel support_level;
+ char *vendor = nullptr;
+ char *renderer = nullptr;
+ char *version = nullptr;
char *support_key = nullptr;
char *gpu_name = nullptr;
public:
- void create_key(eGPUSupportLevel support_level,
- const char *vendor,
- const char *renderer,
- const char *version);
-
- void create_gpu_name(const char *vendor, const char *renderer, const char *version);
+ void init(eGPUDeviceType gpu_device,
+ eGPUOSType os_type,
+ eGPUDriverType driver_type,
+ eGPUSupportLevel gpu_support_level,
+ const char *vendor_str,
+ const char *renderer_str,
+ const char *version_str);
void clear(void);
};
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index bb657ff1645..e2eb8953292 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -57,6 +57,8 @@ static CLG_LogRef LOG = {"gpu.shader"};
using namespace blender;
using namespace blender::gpu;
+static bool gpu_shader_srgb_uniform_dirty_get();
+
/* -------------------------------------------------------------------- */
/** \name Debug functions
* \{ */
@@ -157,7 +159,7 @@ void Shader::print_log(Span<const char *> sources, char *log, const char *stage,
}
/* Print line from the source file that is producing the error. */
if ((error_line != -1) && (error_line != last_error_line || error_char != last_error_char)) {
- const char *src_line_end = src_line;
+ const char *src_line_end;
found_line_id = false;
/* error_line is 1 based in this case. */
int src_line_index = 1;
@@ -288,6 +290,7 @@ static void standard_defines(Vector<const char *> &sources)
GPUShader *GPU_shader_create_ex(const char *vertcode,
const char *fragcode,
const char *geomcode,
+ const char *computecode,
const char *libcode,
const char *defines,
const eGPUShaderTFBType tf_type,
@@ -295,8 +298,10 @@ GPUShader *GPU_shader_create_ex(const char *vertcode,
const int tf_count,
const char *shname)
{
- /* At least a vertex shader and a fragment shader are required. */
- BLI_assert((fragcode != nullptr) && (vertcode != nullptr));
+ /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
+ BLI_assert(((fragcode != nullptr) && (vertcode != nullptr) && (computecode == nullptr)) ||
+ ((fragcode == nullptr) && (vertcode == nullptr) && (geomcode == nullptr) &&
+ (computecode != nullptr)));
Shader *shader = GPUBackend::get()->shader_alloc(shname);
@@ -347,6 +352,21 @@ GPUShader *GPU_shader_create_ex(const char *vertcode,
shader->geometry_shader_from_glsl(sources);
}
+ if (computecode) {
+ Vector<const char *> sources;
+ standard_defines(sources);
+ sources.append("#define GPU_COMPUTE_SHADER\n");
+ if (defines) {
+ sources.append(defines);
+ }
+ if (libcode) {
+ sources.append(libcode);
+ }
+ sources.append(computecode);
+
+ shader->compute_shader_from_glsl(sources);
+ }
+
if (tf_names != nullptr && tf_count > 0) {
BLI_assert(tf_type != GPU_SHADER_TFB_NONE);
shader->transform_feedback_names_set(Span<const char *>(tf_names, tf_count), tf_type);
@@ -378,8 +398,33 @@ GPUShader *GPU_shader_create(const char *vertcode,
const char *defines,
const char *shname)
{
- return GPU_shader_create_ex(
- vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, nullptr, 0, shname);
+ return GPU_shader_create_ex(vertcode,
+ fragcode,
+ geomcode,
+ nullptr,
+ libcode,
+ defines,
+ GPU_SHADER_TFB_NONE,
+ nullptr,
+ 0,
+ shname);
+}
+
+GPUShader *GPU_shader_create_compute(const char *computecode,
+ const char *libcode,
+ const char *defines,
+ const char *shname)
+{
+ return GPU_shader_create_ex(nullptr,
+ nullptr,
+ nullptr,
+ computecode,
+ libcode,
+ defines,
+ GPU_SHADER_TFB_NONE,
+ nullptr,
+ 0,
+ shname);
}
GPUShader *GPU_shader_create_from_python(const char *vertcode,
@@ -400,6 +445,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode,
GPUShader *sh = GPU_shader_create_ex(vertcode,
fragcode,
geomcode,
+ nullptr,
libcode,
defines,
GPU_SHADER_TFB_NONE,
@@ -501,9 +547,13 @@ void GPU_shader_bind(GPUShader *gpu_shader)
GPU_matrix_bind(gpu_shader);
GPU_shader_set_srgb_uniform(gpu_shader);
}
-
- if (GPU_matrix_dirty_get()) {
- GPU_matrix_bind(gpu_shader);
+ else {
+ if (gpu_shader_srgb_uniform_dirty_get()) {
+ GPU_shader_set_srgb_uniform(gpu_shader);
+ }
+ if (GPU_matrix_dirty_get()) {
+ GPU_matrix_bind(gpu_shader);
+ }
}
}
@@ -561,6 +611,13 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin);
}
+int GPU_shader_get_ssbo(GPUShader *shader, const char *name)
+{
+ ShaderInterface *interface = unwrap(shader)->interface;
+ const ShaderInput *ssbo = interface->ssbo_get(name);
+ return ssbo ? ssbo->location : -1;
+}
+
/* DEPRECATED. */
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name)
{
@@ -715,6 +772,12 @@ void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, cons
* \{ */
static int g_shader_builtin_srgb_transform = 0;
+static bool g_shader_builtin_srgb_is_dirty = false;
+
+static bool gpu_shader_srgb_uniform_dirty_get()
+{
+ return g_shader_builtin_srgb_is_dirty;
+}
void GPU_shader_set_srgb_uniform(GPUShader *shader)
{
@@ -722,11 +785,15 @@ void GPU_shader_set_srgb_uniform(GPUShader *shader)
if (loc != -1) {
GPU_shader_uniform_vector_int(shader, loc, 1, 1, &g_shader_builtin_srgb_transform);
}
+ g_shader_builtin_srgb_is_dirty = false;
}
void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear)
{
- g_shader_builtin_srgb_transform = use_srgb_to_linear;
+ if (g_shader_builtin_srgb_transform != use_srgb_to_linear) {
+ g_shader_builtin_srgb_transform = use_srgb_to_linear;
+ g_shader_builtin_srgb_is_dirty = true;
+ }
}
/** \} */
diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc
index 81c1e013877..ae94112b17b 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.cc
+++ b/source/blender/gpu/intern/gpu_shader_interface.cc
@@ -32,10 +32,8 @@
namespace blender::gpu {
-ShaderInterface::ShaderInterface()
-{
- /* TODO(fclem): add unique ID for debugging. */
-}
+/* TODO(fclem): add unique ID for debugging. */
+ShaderInterface::ShaderInterface() = default;
ShaderInterface::~ShaderInterface()
{
@@ -82,6 +80,8 @@ void ShaderInterface::debug_print()
Span<ShaderInput> attrs = Span<ShaderInput>(inputs_, attr_len_);
Span<ShaderInput> ubos = Span<ShaderInput>(inputs_ + attr_len_, ubo_len_);
Span<ShaderInput> uniforms = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_);
+ Span<ShaderInput> ssbos = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_ + uniform_len_,
+ ssbo_len_);
char *name_buf = name_buffer_;
const char format[] = " | %.8x : %4d : %s\n";
@@ -119,6 +119,13 @@ void ShaderInterface::debug_print()
}
}
+ if (ssbos.size() > 0) {
+ printf("\n Shader Storage Objects :\n");
+ }
+ for (const ShaderInput &ssbo : ssbos) {
+ printf(format, ssbo.name_hash, ssbo.binding, name_buf + ssbo.name_offset);
+ }
+
printf("\n");
}
diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh
index aec58544111..ebed7b15170 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.hh
+++ b/source/blender/gpu/intern/gpu_shader_interface.hh
@@ -60,6 +60,7 @@ class ShaderInterface {
uint attr_len_ = 0;
uint ubo_len_ = 0;
uint uniform_len_ = 0;
+ uint ssbo_len_ = 0;
/** Enabled bind-points that needs to be fed with data. */
uint16_t enabled_attr_mask_ = 0;
uint16_t enabled_ubo_mask_ = 0;
@@ -99,6 +100,11 @@ class ShaderInterface {
return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, binding);
}
+ inline const ShaderInput *ssbo_get(const char *name) const
+ {
+ return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name);
+ }
+
inline const char *input_name_get(const ShaderInput *input) const
{
return name_buffer_ + input->name_offset;
diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh
index d9327bbc0f4..281f01dbc22 100644
--- a/source/blender/gpu/intern/gpu_shader_private.hh
+++ b/source/blender/gpu/intern/gpu_shader_private.hh
@@ -49,6 +49,7 @@ class Shader {
virtual void vertex_shader_from_glsl(MutableSpan<const char *> sources) = 0;
virtual void geometry_shader_from_glsl(MutableSpan<const char *> sources) = 0;
virtual void fragment_shader_from_glsl(MutableSpan<const char *> sources) = 0;
+ virtual void compute_shader_from_glsl(MutableSpan<const char *> sources) = 0;
virtual bool finalize(void) = 0;
virtual void transform_feedback_names_set(Span<const char *> name_list,
diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc
index 782d25747bb..5c33066c720 100644
--- a/source/blender/gpu/intern/gpu_state.cc
+++ b/source/blender/gpu/intern/gpu_state.cc
@@ -248,7 +248,7 @@ eGPUWriteMask GPU_write_mask_get()
uint GPU_stencil_mask_get()
{
- GPUStateMutable &state = Context::get()->state_manager->mutable_state;
+ const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
return state.stencil_write_mask;
}
@@ -267,7 +267,7 @@ eGPUStencilTest GPU_stencil_test_get()
/* NOTE: Already premultiplied by U.pixelsize. */
float GPU_line_width_get()
{
- GPUStateMutable &state = Context::get()->state_manager->mutable_state;
+ const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
return state.line_width;
}
@@ -292,7 +292,7 @@ void GPU_viewport_size_get_i(int coords[4])
bool GPU_depth_mask_get()
{
- GPUState &state = Context::get()->state_manager->state;
+ const GPUState &state = Context::get()->state_manager->state;
return (state.write_mask & GPU_WRITE_DEPTH) != 0;
}
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index c3e9058c6c7..997064e82a2 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -60,6 +60,12 @@ Texture::~Texture()
fb_[i]->attachment_remove(fb_attachment_[i]);
}
}
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (this->py_ref) {
+ *this->py_ref = nullptr;
+ }
+#endif
}
bool Texture::init_1D(int w, int layers, eGPUTextureFormat format)
@@ -581,6 +587,19 @@ bool GPU_texture_array(const GPUTexture *tex)
return (reinterpret_cast<const Texture *>(tex)->type_get() & GPU_TEXTURE_ARRAY) != 0;
}
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_texture_py_reference_get(GPUTexture *tex)
+{
+ return unwrap(tex)->py_ref;
+}
+
+void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref)
+{
+ BLI_assert(py_ref == nullptr || unwrap(tex)->py_ref == nullptr);
+ unwrap(tex)->py_ref = py_ref;
+}
+#endif
+
/* TODO remove */
int GPU_texture_opengl_bindcode(const GPUTexture *tex)
{
diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
index 3d808bce152..a8f2e482bdd 100644
--- a/source/blender/gpu/intern/gpu_texture_private.hh
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -80,6 +80,13 @@ class Texture {
int refcount = 1;
/** Width & Height (of source data), optional. */
int src_w = 0, src_h = 0;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ /**
+ * Reference of a pointer that needs to be cleaned when deallocating the texture.
+ * Points to #BPyGPUTexture.tex
+ */
+ void **py_ref = nullptr;
+#endif
protected:
/* ---- Texture format (immutable after init). ---- */
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc
index 09b9eba9f95..3ecbb740a0c 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.cc
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc
@@ -149,6 +149,16 @@ GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts_)
return wrap(unwrap(verts_)->duplicate());
}
+const void *GPU_vertbuf_read(GPUVertBuf *verts)
+{
+ return unwrap(verts)->read();
+}
+
+void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data)
+{
+ return unwrap(verts)->unmap(mapped_data);
+}
+
/** Same as discard but does not free. */
void GPU_vertbuf_clear(GPUVertBuf *verts)
{
@@ -324,6 +334,11 @@ void GPU_vertbuf_use(GPUVertBuf *verts)
unwrap(verts)->upload();
}
+void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding)
+{
+ unwrap(verts)->bind_as_ssbo(binding);
+}
+
/* XXX this is just a wrapper for the use of the Hair refine workaround.
* To be used with GPU_vertbuf_use(). */
void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data)
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
index 67a09f6f83c..9531c2c1a5f 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
@@ -66,6 +66,7 @@ class VertBuf {
void allocate(uint vert_len);
void resize(uint vert_len);
void upload(void);
+ virtual void bind_as_ssbo(uint binding) = 0;
VertBuf *duplicate(void);
@@ -96,6 +97,8 @@ class VertBuf {
}
virtual void update_sub(uint start, uint len, void *data) = 0;
+ virtual const void *read() const = 0;
+ virtual void *unmap(const void *mapped_data) const = 0;
protected:
virtual void acquire_data(void) = 0;
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index ef7788194a1..d85f9f7684d 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -41,39 +41,42 @@ namespace blender::gpu {
void GLBackend::platform_init()
{
BLI_assert(!GPG.initialized);
- GPG.initialized = true;
+
+ const char *vendor = (const char *)glGetString(GL_VENDOR);
+ const char *renderer = (const char *)glGetString(GL_RENDERER);
+ const char *version = (const char *)glGetString(GL_VERSION);
+ eGPUDeviceType device = GPU_DEVICE_ANY;
+ eGPUOSType os = GPU_OS_ANY;
+ eGPUDriverType driver = GPU_DRIVER_ANY;
+ eGPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED;
#ifdef _WIN32
- GPG.os = GPU_OS_WIN;
+ os = GPU_OS_WIN;
#elif defined(__APPLE__)
- GPG.os = GPU_OS_MAC;
+ os = GPU_OS_MAC;
#else
- GPG.os = GPU_OS_UNIX;
+ os = GPU_OS_UNIX;
#endif
- const char *vendor = (const char *)glGetString(GL_VENDOR);
- const char *renderer = (const char *)glGetString(GL_RENDERER);
- const char *version = (const char *)glGetString(GL_VERSION);
-
if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
- GPG.device = GPU_DEVICE_ATI;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_ATI;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(vendor, "NVIDIA")) {
- GPG.device = GPU_DEVICE_NVIDIA;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_NVIDIA;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(vendor, "Intel") ||
/* src/mesa/drivers/dri/intel/intel_context.c */
strstr(renderer, "Mesa DRI Intel") || strstr(renderer, "Mesa DRI Mobile Intel")) {
- GPG.device = GPU_DEVICE_INTEL;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_INTEL;
+ driver = GPU_DRIVER_OFFICIAL;
if (strstr(renderer, "UHD Graphics") ||
/* Not UHD but affected by the same bugs. */
strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2") ||
strstr(renderer, "Whiskey Lake")) {
- GPG.device |= GPU_DEVICE_INTEL_UHD;
+ device |= GPU_DEVICE_INTEL_UHD;
}
}
else if ((strstr(renderer, "Mesa DRI R")) ||
@@ -81,49 +84,47 @@ void GLBackend::platform_init()
(strstr(renderer, "AMD") && strstr(vendor, "X.Org")) ||
(strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) ||
(strstr(renderer, "Gallium ") && strstr(renderer, " on AMD "))) {
- GPG.device = GPU_DEVICE_ATI;
- GPG.driver = GPU_DRIVER_OPENSOURCE;
+ device = GPU_DEVICE_ATI;
+ driver = GPU_DRIVER_OPENSOURCE;
}
else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) {
- GPG.device = GPU_DEVICE_NVIDIA;
- GPG.driver = GPU_DRIVER_OPENSOURCE;
+ device = GPU_DEVICE_NVIDIA;
+ driver = GPU_DRIVER_OPENSOURCE;
}
else if (strstr(vendor, "Mesa")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(vendor, "Microsoft")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(vendor, "Apple")) {
/* Apple Silicon. */
- GPG.device = GPU_DEVICE_APPLE;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_APPLE;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(renderer, "Apple Software Renderer")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(renderer, "llvmpipe") || strstr(renderer, "softpipe")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else {
printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n");
printf("Detected OpenGL configuration:\n");
printf("Vendor: %s\n", vendor);
printf("Renderer: %s\n", renderer);
- GPG.device = GPU_DEVICE_ANY;
- GPG.driver = GPU_DRIVER_ANY;
}
/* Detect support level */
if (!GLEW_VERSION_3_3) {
- GPG.support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
+ support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
}
else {
- if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY)) {
+ if ((device & GPU_DEVICE_INTEL) && (os & GPU_OS_WIN)) {
/* Old Intel drivers with known bugs that cause material properties to crash.
* Version Build 10.18.14.5067 is the latest available and appears to be working
* ok with our workarounds, so excluded from this list. */
@@ -132,19 +133,19 @@ void GLBackend::platform_init()
strstr(version, "Build 9.18") || strstr(version, "Build 10.18.10.3") ||
strstr(version, "Build 10.18.10.4") || strstr(version, "Build 10.18.10.5") ||
strstr(version, "Build 10.18.14.4")) {
- GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED;
+ support_level = GPU_SUPPORT_LEVEL_LIMITED;
}
}
- if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
+ if ((device & GPU_DEVICE_ATI) && (os & GPU_OS_UNIX)) {
/* Platform seems to work when SB backend is disabled. This can be done
* by adding the environment variable `R600_DEBUG=nosb`. */
if (strstr(renderer, "AMD CEDAR")) {
- GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED;
+ support_level = GPU_SUPPORT_LEVEL_LIMITED;
}
}
}
- GPG.create_key(GPG.support_level, vendor, renderer, version);
- GPG.create_gpu_name(vendor, renderer, version);
+
+ GPG.init(device, os, driver, support_level, vendor, renderer, version);
}
void GLBackend::platform_exit()
@@ -204,6 +205,11 @@ static bool detect_mip_render_workaround()
return enable_workaround;
}
+static const char *gl_extension_get(int i)
+{
+ return (char *)glGetStringi(GL_EXTENSIONS, i);
+}
+
static void detect_workarounds()
{
const char *vendor = (const char *)glGetString(GL_VENDOR);
@@ -286,8 +292,9 @@ static void detect_workarounds()
strstr(renderer, " RX 480 ") || strstr(renderer, " RX 490 ") ||
strstr(renderer, " RX 560 ") || strstr(renderer, " RX 560X ") ||
strstr(renderer, " RX 570 ") || strstr(renderer, " RX 580 ") ||
- strstr(renderer, " RX 590 ") || strstr(renderer, " RX550/550 ") ||
- strstr(renderer, " (TM) 520 ") || strstr(renderer, " (TM) 530 ") ||
+ strstr(renderer, " RX 580X ") || strstr(renderer, " RX 590 ") ||
+ strstr(renderer, " RX550/550 ") || strstr(renderer, "(TM) 520 ") ||
+ strstr(renderer, "(TM) 530 ") || strstr(renderer, "(TM) 535 ") ||
strstr(renderer, " R5 ") || strstr(renderer, " R7 ") || strstr(renderer, " R9 ")) {
GCaps.use_hq_normals_workaround = true;
}
@@ -418,8 +425,28 @@ void GLBackend::capabilities_init()
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &GCaps.max_textures_vert);
glGetIntegerv(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, &GCaps.max_textures_geom);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &GCaps.max_textures);
+ glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &GCaps.max_uniforms_vert);
+ glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &GCaps.max_uniforms_frag);
+ glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &GCaps.max_batch_indices);
+ glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &GCaps.max_batch_vertices);
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &GCaps.max_vertex_attribs);
+ glGetIntegerv(GL_MAX_VARYING_FLOATS, &GCaps.max_varying_floats);
+
+ glGetIntegerv(GL_NUM_EXTENSIONS, &GCaps.extensions_len);
+ GCaps.extension_get = gl_extension_get;
+
GCaps.mem_stats_support = GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo;
GCaps.shader_image_load_store_support = GLEW_ARB_shader_image_load_store;
+ GCaps.compute_shader_support = GLEW_ARB_compute_shader;
+ if (GCaps.compute_shader_support) {
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &GCaps.max_work_group_count[0]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &GCaps.max_work_group_count[1]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &GCaps.max_work_group_count[2]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]);
+ }
+ GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object;
/* GL specific capabilities. */
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size);
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GLContext::max_cubemap_size);
diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh
index 231e5811b45..e9dcdffced0 100644
--- a/source/blender/gpu/opengl/gl_backend.hh
+++ b/source/blender/gpu/opengl/gl_backend.hh
@@ -28,6 +28,7 @@
#include "BLI_vector.hh"
#include "gl_batch.hh"
+#include "gl_compute.hh"
#include "gl_context.hh"
#include "gl_drawlist.hh"
#include "gl_framebuffer.hh"
@@ -126,6 +127,12 @@ class GLBackend : public GPUBackend {
return shared_orphan_list_;
};
+ void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override
+ {
+ GLContext::get()->state_manager_active_get()->apply_state();
+ GLCompute::dispatch(groups_x_len, groups_y_len, groups_z_len);
+ }
+
private:
static void platform_init(void);
static void platform_exit(void);
diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc
index 321d9552828..fc1d1006d0d 100644
--- a/source/blender/gpu/opengl/gl_batch.cc
+++ b/source/blender/gpu/opengl/gl_batch.cc
@@ -44,7 +44,7 @@
using namespace blender::gpu;
/* -------------------------------------------------------------------- */
-/** \name Vao cache
+/** \name VAO Cache
*
* Each #GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration.
* TODO(fclem): Could be revisited to avoid so much cross references.
@@ -279,20 +279,6 @@ GLuint GLVaoCache::vao_get(GPUBatch *batch)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Creation & Deletion
- * \{ */
-
-GLBatch::GLBatch()
-{
-}
-
-GLBatch::~GLBatch()
-{
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Drawing
* \{ */
diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh
index 218b9ffe4b7..704b6471dd5 100644
--- a/source/blender/gpu/opengl/gl_batch.hh
+++ b/source/blender/gpu/opengl/gl_batch.hh
@@ -43,7 +43,7 @@ class GLShaderInterface;
#define GPU_VAO_STATIC_LEN 3
-/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer)
+/* VAO management: remembers all geometry state (vertex attribute bindings & element buffer)
* for each shader interface. Start with a static number of vaos and fallback to dynamic count
* if necessary. Once a batch goes dynamic it does not go back. */
class GLVaoCache {
@@ -96,9 +96,6 @@ class GLBatch : public Batch {
GLVaoCache vao_cache_;
public:
- GLBatch();
- ~GLBatch();
-
void draw(int v_first, int v_count, int i_first, int i_count) override;
void bind(int i_first);
diff --git a/source/blender/gpu/opengl/gl_compute.cc b/source/blender/gpu/opengl/gl_compute.cc
new file mode 100644
index 00000000000..fa8317dde4a
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_compute.cc
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "gl_compute.hh"
+
+#include "gl_debug.hh"
+
+#include "glew-mx.h"
+
+namespace blender::gpu {
+
+void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len)
+{
+ glDispatchCompute(group_x_len, group_y_len, group_z_len);
+ debug::check_gl_error("Dispatch Compute");
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_compute.hh b/source/blender/gpu/opengl/gl_compute.hh
new file mode 100644
index 00000000000..2fd918ddd10
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_compute.hh
@@ -0,0 +1,30 @@
+/*
+ * 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 gpu
+ */
+
+#pragma once
+
+namespace blender::gpu {
+
+class GLCompute {
+ public:
+ static void dispatch(int group_x_len, int group_y_len, int group_z_len);
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_drawlist.cc b/source/blender/gpu/opengl/gl_drawlist.cc
index d3401036154..50ab5574cd2 100644
--- a/source/blender/gpu/opengl/gl_drawlist.cc
+++ b/source/blender/gpu/opengl/gl_drawlist.cc
@@ -68,6 +68,7 @@ GLDrawList::GLDrawList(int length)
batch_ = nullptr;
buffer_id_ = 0;
command_len_ = 0;
+ base_index_ = 0;
command_offset_ = 0;
data_size_ = 0;
data_ = nullptr;
diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh
index d0bf0fcdf1f..b42f439f9ec 100644
--- a/source/blender/gpu/opengl/gl_immediate.hh
+++ b/source/blender/gpu/opengl/gl_immediate.hh
@@ -38,7 +38,7 @@ namespace blender::gpu {
class GLImmediate : public Immediate {
private:
- /* Use two buffers for strict and unstrict vertex count to
+ /* Use two buffers for strict and non-strict vertex count to
* avoid some huge driver slowdown (see T70922).
* Use accessor functions to get / modify. */
struct {
diff --git a/source/blender/gpu/opengl/gl_index_buffer.cc b/source/blender/gpu/opengl/gl_index_buffer.cc
index e2c18c5d0b9..e305f765ad9 100644
--- a/source/blender/gpu/opengl/gl_index_buffer.cc
+++ b/source/blender/gpu/opengl/gl_index_buffer.cc
@@ -40,17 +40,14 @@ void GLIndexBuf::bind()
return;
}
- if (ibo_id_ == 0) {
+ const bool allocate_on_device = ibo_id_ == 0;
+ if (allocate_on_device) {
glGenBuffers(1, &ibo_id_);
-
- if (data_ == nullptr) {
- debug::raise_gl_error("Trying to use Index Buffer but the buffer contains no data");
- }
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_);
- if (data_ != nullptr) {
+ if (data_ != nullptr || allocate_on_device) {
size_t size = this->size_get();
/* Sends data to GPU. */
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data_, GL_STATIC_DRAW);
@@ -59,4 +56,29 @@ void GLIndexBuf::bind()
}
}
+void GLIndexBuf::bind_as_ssbo(uint binding)
+{
+ bind();
+ BLI_assert(ibo_id_ != 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ibo_id_);
+}
+
+const uint32_t *GLIndexBuf::read() const
+{
+ BLI_assert(is_active());
+ void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
+ uint32_t *result = static_cast<uint32_t *>(data);
+ return result;
+}
+
+bool GLIndexBuf::is_active() const
+{
+ if (!ibo_id_) {
+ return false;
+ }
+ int active_ibo_id = 0;
+ glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &active_ibo_id);
+ return ibo_id_ == active_ibo_id;
+}
+
} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_index_buffer.hh b/source/blender/gpu/opengl/gl_index_buffer.hh
index b84934bb77f..0dbdaa6d398 100644
--- a/source/blender/gpu/opengl/gl_index_buffer.hh
+++ b/source/blender/gpu/opengl/gl_index_buffer.hh
@@ -34,6 +34,7 @@ namespace blender::gpu {
class GLIndexBuf : public IndexBuf {
friend class GLBatch;
friend class GLDrawList;
+ friend class GLShader; /* For compute shaders. */
private:
GLuint ibo_id_ = 0;
@@ -42,6 +43,9 @@ class GLIndexBuf : public IndexBuf {
~GLIndexBuf();
void bind(void);
+ void bind_as_ssbo(uint binding) override;
+
+ const uint32_t *read() const override;
void *offset_ptr(uint additional_vertex_offset) const
{
@@ -57,6 +61,9 @@ class GLIndexBuf : public IndexBuf {
return (index_type_ == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu;
}
+ private:
+ bool is_active() const;
+
MEM_CXX_CLASS_ALLOC_FUNCS("GLIndexBuf")
};
diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc
index dd08a67517e..e77347d99eb 100644
--- a/source/blender/gpu/opengl/gl_shader.cc
+++ b/source/blender/gpu/opengl/gl_shader.cc
@@ -26,6 +26,7 @@
#include "BLI_string.h"
#include "BLI_vector.hh"
+#include "GPU_capabilities.h"
#include "GPU_platform.h"
#include "gl_backend.hh"
@@ -63,6 +64,7 @@ GLShader::~GLShader()
glDeleteShader(vert_shader_);
glDeleteShader(geom_shader_);
glDeleteShader(frag_shader_);
+ glDeleteShader(compute_shader_);
glDeleteProgram(shader_program_);
}
@@ -72,7 +74,7 @@ GLShader::~GLShader()
/** \name Shader stage creation
* \{ */
-char *GLShader::glsl_patch_get()
+static char *glsl_patch_default_get()
{
/** Used for shader patching. Init once. */
static char patch[512] = "\0";
@@ -111,6 +113,30 @@ char *GLShader::glsl_patch_get()
return patch;
}
+static char *glsl_patch_compute_get()
+{
+ /** Used for shader patching. Init once. */
+ static char patch[512] = "\0";
+ if (patch[0] != '\0') {
+ return patch;
+ }
+
+ size_t slen = 0;
+ /* Version need to go first. */
+ STR_CONCAT(patch, slen, "#version 430\n");
+ STR_CONCAT(patch, slen, "#extension GL_ARB_compute_shader :enable\n");
+ BLI_assert(slen < sizeof(patch));
+ return patch;
+}
+
+char *GLShader::glsl_patch_get(GLenum gl_stage)
+{
+ if (gl_stage == GL_COMPUTE_SHADER) {
+ return glsl_patch_compute_get();
+ }
+ return glsl_patch_default_get();
+}
+
/* Create, compile and attach the shader stage to the shader program. */
GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources)
{
@@ -121,7 +147,7 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *>
}
/* Patch the shader code using the first source slot. */
- sources[0] = glsl_patch_get();
+ sources[0] = glsl_patch_get(gl_stage);
glShaderSource(shader, sources.size(), sources.data(), nullptr);
glCompileShader(shader);
@@ -142,6 +168,9 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *>
case GL_FRAGMENT_SHADER:
this->print_log(sources, log, "FragShader", !status);
break;
+ case GL_COMPUTE_SHADER:
+ this->print_log(sources, log, "ComputeShader", !status);
+ break;
}
}
}
@@ -172,6 +201,11 @@ void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources);
}
+void GLShader::compute_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ compute_shader_ = this->create_shader_stage(GL_COMPUTE_SHADER, sources);
+}
+
bool GLShader::finalize()
{
if (compilation_failed_) {
diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh
index 152eb2f068a..48aaaf2283d 100644
--- a/source/blender/gpu/opengl/gl_shader.hh
+++ b/source/blender/gpu/opengl/gl_shader.hh
@@ -43,6 +43,7 @@ class GLShader : public Shader {
GLuint vert_shader_ = 0;
GLuint geom_shader_ = 0;
GLuint frag_shader_ = 0;
+ GLuint compute_shader_ = 0;
/** True if any shader failed to compile. */
bool compilation_failed_ = false;
@@ -56,6 +57,7 @@ class GLShader : public Shader {
void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
void fragment_shader_from_glsl(MutableSpan<const char *> sources) override;
+ void compute_shader_from_glsl(MutableSpan<const char *> sources) override;
bool finalize(void) override;
void transform_feedback_names_set(Span<const char *> name_list,
@@ -75,7 +77,7 @@ class GLShader : public Shader {
int program_handle_get(void) const override;
private:
- char *glsl_patch_get(void);
+ char *glsl_patch_get(GLenum gl_stage);
GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources);
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
index 5870c645bf4..9cf072b2e8a 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.cc
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -29,6 +29,8 @@
#include "gl_shader_interface.hh"
+#include "GPU_capabilities.h"
+
namespace blender::gpu {
/* -------------------------------------------------------------------- */
@@ -125,6 +127,18 @@ static inline int image_binding(int32_t program,
return -1;
}
}
+
+static inline int ssbo_binding(int32_t program, uint32_t ssbo_index)
+{
+ GLint binding = -1;
+ GLenum property = GL_BUFFER_BINDING;
+ GLint values_written = 0;
+ glGetProgramResourceiv(
+ program, GL_SHADER_STORAGE_BLOCK, ssbo_index, 1, &property, 1, &values_written, &binding);
+
+ return binding;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -149,6 +163,13 @@ GLShaderInterface::GLShaderInterface(GLuint program)
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len);
uniform_len = active_uniform_len;
+ GLint max_ssbo_name_len = 0, ssbo_len = 0;
+ if (GPU_shader_storage_buffer_objects_support()) {
+ glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &ssbo_len);
+ glGetProgramInterfaceiv(
+ program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_len);
+ }
+
BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t");
/* Work around driver bug with Intel HD 4600 on Windows 7/8, where
@@ -162,6 +183,9 @@ GLShaderInterface::GLShaderInterface(GLuint program)
if (uniform_len > 0 && max_uniform_name_len == 0) {
max_uniform_name_len = 256;
}
+ if (ssbo_len > 0 && max_ssbo_name_len == 0) {
+ max_ssbo_name_len = 256;
+ }
/* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before
* allocating the uniform array. */
@@ -186,11 +210,12 @@ GLShaderInterface::GLShaderInterface(GLuint program)
}
MEM_freeN(ubo_uni_ids);
- int input_tot_len = attr_len + ubo_len + uniform_len;
+ int input_tot_len = attr_len + ubo_len + uniform_len + ssbo_len;
inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__);
const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len +
- uniform_len * max_uniform_name_len;
+ uniform_len * max_uniform_name_len +
+ ssbo_len * max_ssbo_name_len;
name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer");
uint32_t name_buffer_offset = 0;
@@ -257,6 +282,22 @@ GLShaderInterface::GLShaderInterface(GLuint program)
}
}
+ /* SSBOs */
+ for (int i = 0; i < ssbo_len; i++) {
+ char *name = name_buffer_ + name_buffer_offset;
+ GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
+ GLsizei name_len = 0;
+ glGetProgramResourceName(
+ program, GL_SHADER_STORAGE_BLOCK, i, remaining_buffer, &name_len, name);
+
+ const GLint binding = ssbo_binding(program, i);
+
+ ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_++];
+ input->binding = input->location = binding;
+
+ name_buffer_offset += this->set_input_name(input, name, name_len);
+ }
+
/* Builtin Uniforms */
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh
index 651c3c22afa..3b4b40b1d10 100644
--- a/source/blender/gpu/opengl/gl_state.hh
+++ b/source/blender/gpu/opengl/gl_state.hh
@@ -121,6 +121,9 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits)
if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) {
barrier |= GL_TEXTURE_FETCH_BARRIER_BIT;
}
+ if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) {
+ barrier |= GL_SHADER_STORAGE_BARRIER_BIT;
+ }
return barrier;
}
diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc
index 51cfcd20a6c..e2478a9976c 100644
--- a/source/blender/gpu/opengl/gl_texture.cc
+++ b/source/blender/gpu/opengl/gl_texture.cc
@@ -290,7 +290,9 @@ void GLTexture::update_sub(
}
}
-/** This will create the mipmap images and populate them with filtered data from base level.
+/**
+ * This will create the mipmap images and populate them with filtered data from base level.
+ *
* WARNING: Depth textures are not populated but they have their mips correctly defined.
* WARNING: This resets the mipmap range.
*/
@@ -366,7 +368,7 @@ void GLTexture::copy_to(Texture *dst_)
void *GLTexture::read(int mip, eGPUDataFormat type)
{
BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED));
- BLI_assert(mip <= mipmaps_);
+ BLI_assert(mip <= mipmaps_ || mip == 0);
BLI_assert(validate_data_format(format_, type));
/* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc
index a56d5269fde..ce16a491528 100644
--- a/source/blender/gpu/opengl/gl_vertex_buffer.cc
+++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc
@@ -29,6 +29,10 @@ namespace blender::gpu {
void GLVertBuf::acquire_data()
{
+ if (usage_ == GPU_USAGE_DEVICE_ONLY) {
+ return;
+ }
+
/* Discard previous data if any. */
MEM_SAFE_FREE(data);
data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__);
@@ -36,6 +40,10 @@ void GLVertBuf::acquire_data()
void GLVertBuf::resize_data()
{
+ if (usage_ == GPU_USAGE_DEVICE_ONLY) {
+ return;
+ }
+
data = (uchar *)MEM_reallocN(data, sizeof(uchar) * this->size_alloc_get());
}
@@ -94,8 +102,10 @@ void GLVertBuf::bind()
vbo_size_ = this->size_used_get();
/* Orphan the vbo to avoid sync then upload data. */
glBufferData(GL_ARRAY_BUFFER, vbo_size_, nullptr, to_gl(usage_));
- glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data);
-
+ /* Do not transfer data from host to device when buffer is device only. */
+ if (usage_ != GPU_USAGE_DEVICE_ONLY) {
+ glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data);
+ }
memory_usage += vbo_size_;
if (usage_ == GPU_USAGE_STATIC) {
@@ -106,6 +116,37 @@ void GLVertBuf::bind()
}
}
+void GLVertBuf::bind_as_ssbo(uint binding)
+{
+ bind();
+ BLI_assert(vbo_id_ != 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_);
+}
+
+const void *GLVertBuf::read() const
+{
+ BLI_assert(is_active());
+ void *result = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
+ return result;
+}
+
+void *GLVertBuf::unmap(const void *mapped_data) const
+{
+ void *result = MEM_mallocN(vbo_size_, __func__);
+ memcpy(result, mapped_data, vbo_size_);
+ return result;
+}
+
+bool GLVertBuf::is_active() const
+{
+ if (!vbo_id_) {
+ return false;
+ }
+ int active_vbo_id = 0;
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &active_vbo_id);
+ return vbo_id_ == active_vbo_id;
+}
+
void GLVertBuf::update_sub(uint start, uint len, void *data)
{
glBufferSubData(GL_ARRAY_BUFFER, start, len, data);
diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh
index e2bf6cd00e8..6c38a2225b3 100644
--- a/source/blender/gpu/opengl/gl_vertex_buffer.hh
+++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh
@@ -47,12 +47,19 @@ class GLVertBuf : public VertBuf {
void update_sub(uint start, uint len, void *data) override;
+ const void *read() const override;
+ void *unmap(const void *mapped_data) const override;
+
protected:
void acquire_data(void) override;
void resize_data(void) override;
void release_data(void) override;
void upload_data(void) override;
void duplicate_data(VertBuf *dst) override;
+ void bind_as_ssbo(uint binding) override;
+
+ private:
+ bool is_active() const;
MEM_CXX_CLASS_ALLOC_FUNCS("GLVertBuf");
};
@@ -65,6 +72,7 @@ static inline GLenum to_gl(GPUUsageType type)
case GPU_USAGE_DYNAMIC:
return GL_DYNAMIC_DRAW;
case GPU_USAGE_STATIC:
+ case GPU_USAGE_DEVICE_ONLY:
return GL_STATIC_DRAW;
default:
BLI_assert(0);
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
index 81e0965fad3..b5036b51d9d 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
@@ -61,11 +61,27 @@ vec2 do_widget(void)
const vec2 ofs = vec2(0.5, -0.5);
lineWidth = abs(rect.x - recti.x);
vec2 emboss_ofs = vec2(0.0, -lineWidth);
- vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs + ofs.yy,
- rect.xw + ofs.yx,
- rect.yz + emboss_ofs + ofs.xy,
- rect.yw + ofs.xx);
- vec2 pos = v_pos[gl_VertexID];
+
+ vec2 pos;
+ switch (gl_VertexID) {
+ default:
+ case 0: {
+ pos = rect.xz + emboss_ofs + ofs.yy;
+ break;
+ }
+ case 1: {
+ pos = rect.xw + ofs.yx;
+ break;
+ }
+ case 2: {
+ pos = rect.yz + emboss_ofs + ofs.xy;
+ break;
+ }
+ case 3: {
+ pos = rect.yw + ofs.xx;
+ break;
+ }
+ }
uvInterp = pos - rect.xz;
outRectSize = rect.yw - rect.xz;
diff --git a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
index d85884e0a25..2568cd74445 100644
--- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
@@ -7,7 +7,7 @@ flat in int interp_size;
out vec4 fragColor;
-uniform sampler1DArray glyph;
+uniform sampler2D glyph;
const vec2 offsets4[4] = vec2[4](
vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(-0.5, -0.5), vec2(-0.5, -0.5));
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
index 4db27c3049d..a14ff5021bf 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
@@ -37,8 +37,13 @@ void node_geometry(vec3 I,
normal = (toworld * vec4(N, 0.0)).xyz;
true_normal = normal;
# endif
+
+# ifdef HAIR_SHADER
+ tangent = -hairTangent;
+# else
tangent_orco_z(orco, orco);
node_tangent(N, orco, objmat, tangent);
+# endif
parametric = vec3(barycentric, 0.0);
backfacing = (gl_FrontFacing) ? 0.0 : 1.0;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
index 2e0515e324e..d77259638fd 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
@@ -55,6 +55,7 @@ void node_bsdf_principled(vec4 base_color,
float specular_weight = (1.0 - transmission);
clearcoat = max(clearcoat, 0.0);
transmission_roughness = 1.0 - (1.0 - roughness) * (1.0 - transmission_roughness);
+ specular = max(0.0, specular);
CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
index 5a68f802659..5129bf71903 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
@@ -23,8 +23,8 @@ void node_subsurface_scattering(vec4 color,
/* Not perfect for texture_blur values between 0.0 and 1.0.
* Interpolate between separated color and color applied on irradiance. */
float one_minus_texture_blur = 1.0 - texture_blur;
- vec3 sss_albedo = color.rgb * texture_blur + one_minus_texture_blur;
- vec3 radiance_tint = color.rgb * one_minus_texture_blur + texture_blur;
+ vec3 sss_albedo = color.rgb * one_minus_texture_blur + texture_blur;
+ vec3 radiance_tint = color.rgb * texture_blur + one_minus_texture_blur;
/* Consider output radiance as irradiance. */
out_Diffuse_0.radiance *= radiance_tint;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
index d8d9ecdf287..5745f11ede4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
@@ -2,7 +2,7 @@
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
- * bad precision and not to small to be noticeable. We use float seed because
+ * bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
index 60ed098beb3..4ad5d4232de 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
@@ -150,3 +150,9 @@ void vector_math_faceforward(
{
outVector = faceforward(a, b, c);
}
+
+void vector_math_multiply_add(
+ vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue)
+{
+ outVector = a * b + c;
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl
new file mode 100644
index 00000000000..2c5d38eabbe
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl
@@ -0,0 +1,15 @@
+void node_wavelength(float wavelength,
+ sampler1DArray spectrummap,
+ float layer,
+ vec3 xyz_to_r,
+ vec3 xyz_to_g,
+ vec3 xyz_to_b,
+ out vec4 color)
+{
+ mat3 xyz_to_rgb = mat3(xyz_to_r, xyz_to_g, xyz_to_b);
+ float t = (wavelength - 380.0) / (780.0 - 380.0);
+ vec3 xyz = texture(spectrummap, vec2(t, layer)).rgb;
+ vec3 rgb = xyz * xyz_to_rgb;
+ rgb *= 1.0 / 2.52; /* Empirical scale from lg to make all comps <= 1. */
+ color = vec4(clamp(rgb, 0.0, 1.0), 1.0);
+}
diff --git a/source/blender/gpu/tests/gpu_index_buffer_test.cc b/source/blender/gpu/tests/gpu_index_buffer_test.cc
new file mode 100644
index 00000000000..78e351af7f1
--- /dev/null
+++ b/source/blender/gpu/tests/gpu_index_buffer_test.cc
@@ -0,0 +1,47 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "GPU_index_buffer.h"
+
+#include "gpu_testing.hh"
+
+namespace blender::gpu::tests {
+
+TEST_F(GPUTest, gpu_index_buffer_subbuilders)
+{
+ const uint num_subbuilders = 10;
+ const uint verts_per_subbuilders = 100;
+ const uint vertex_len = num_subbuilders * verts_per_subbuilders;
+
+ GPUIndexBufBuilder builder;
+ GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, vertex_len, vertex_len);
+
+ GPUIndexBufBuilder subbuilders[num_subbuilders];
+ for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
+ memcpy(&subbuilders[subbuilder_index], &builder, sizeof(builder));
+ }
+
+ for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
+ GPUIndexBufBuilder &subbuilder = subbuilders[subbuilder_index];
+ for (int subbuilder_vert_index = 0; subbuilder_vert_index < verts_per_subbuilders;
+ subbuilder_vert_index++) {
+ int vert_index_to_update = subbuilder_index * verts_per_subbuilders + subbuilder_vert_index;
+ GPU_indexbuf_set_point_vert(&subbuilder, vert_index_to_update, vert_index_to_update);
+ }
+ }
+
+ for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
+ EXPECT_EQ(builder.index_len, subbuilder_index * verts_per_subbuilders);
+ GPU_indexbuf_join(&builder, &subbuilders[subbuilder_index]);
+ EXPECT_EQ(builder.index_len, (subbuilder_index + 1) * verts_per_subbuilders);
+ }
+
+ GPUIndexBuf *index_buffer = GPU_indexbuf_build(&builder);
+ EXPECT_NE(index_buffer, nullptr);
+ GPU_INDEXBUF_DISCARD_SAFE(index_buffer);
+}
+
+} // namespace blender::gpu::tests
diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc
new file mode 100644
index 00000000000..e8645b89e41
--- /dev/null
+++ b/source/blender/gpu/tests/gpu_shader_test.cc
@@ -0,0 +1,301 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "GPU_capabilities.h"
+#include "GPU_compute.h"
+#include "GPU_index_buffer.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+#include "GPU_vertex_buffer.h"
+#include "GPU_vertex_format.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "gpu_testing.hh"
+
+#include "GPU_glew.h"
+
+namespace blender::gpu::tests {
+
+TEST_F(GPUTest, gpu_shader_compute_2d)
+{
+
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ static constexpr uint SIZE = 512;
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1, local_size_y = 1) in;
+layout(rgba32f, binding = 0) uniform image2D img_output;
+
+void main() {
+ vec4 pixel = vec4(1.0, 0.5, 0.2, 1.0);
+ imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel);
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(
+ compute_glsl, nullptr, nullptr, "gpu_shader_compute_2d");
+ EXPECT_NE(shader, nullptr);
+
+ /* Create texture to store result and attach to shader. */
+ GPUTexture *texture = GPU_texture_create_2d(
+ "gpu_shader_compute_2d", SIZE, SIZE, 0, GPU_RGBA32F, nullptr);
+ EXPECT_NE(texture, nullptr);
+
+ GPU_shader_bind(shader);
+ GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "img_output"));
+
+ /* Dispatch compute task. */
+ GPU_compute_dispatch(shader, SIZE, SIZE, 1);
+
+ /* Check if compute has been done. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
+ EXPECT_NE(data, nullptr);
+ for (int index = 0; index < SIZE * SIZE; index++) {
+ EXPECT_FLOAT_EQ(data[index * 4 + 0], 1.0f);
+ EXPECT_FLOAT_EQ(data[index * 4 + 1], 0.5f);
+ EXPECT_FLOAT_EQ(data[index * 4 + 2], 0.2f);
+ EXPECT_FLOAT_EQ(data[index * 4 + 3], 1.0f);
+ }
+ MEM_freeN(data);
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
+ GPU_shader_free(shader);
+}
+
+TEST_F(GPUTest, gpu_shader_compute_1d)
+{
+
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ static constexpr uint SIZE = 10;
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1) in;
+
+layout(rgba32f, binding = 1) uniform image1D outputVboData;
+
+void main() {
+ int index = int(gl_GlobalInvocationID.x);
+ vec4 pos = vec4(gl_GlobalInvocationID.x);
+ imageStore(outputVboData, index, pos);
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(
+ compute_glsl, nullptr, nullptr, "gpu_shader_compute_1d");
+ EXPECT_NE(shader, nullptr);
+
+ /* Construct Texture. */
+ GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, NULL);
+ EXPECT_NE(texture, nullptr);
+
+ GPU_shader_bind(shader);
+ GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "outputVboData"));
+
+ /* Dispatch compute task. */
+ GPU_compute_dispatch(shader, SIZE, 1, 1);
+
+ /* Check if compute has been done. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+
+ /* Create texture to load back result. */
+ float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
+ EXPECT_NE(data, nullptr);
+ for (int index = 0; index < SIZE; index++) {
+ float expected_value = index;
+ EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value);
+ }
+ MEM_freeN(data);
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
+ GPU_shader_free(shader);
+}
+
+TEST_F(GPUTest, gpu_shader_compute_vbo)
+{
+
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ static constexpr uint SIZE = 128;
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1) in;
+
+layout(std430, binding = 0) writeonly buffer outputVboData
+{
+ vec4 out_positions[];
+};
+
+void main() {
+ uint index = gl_GlobalInvocationID.x;
+ vec4 pos = vec4(gl_GlobalInvocationID.x);
+ out_positions[index] = pos;
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(
+ compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo");
+ EXPECT_NE(shader, nullptr);
+ GPU_shader_bind(shader);
+
+ /* Construct VBO. */
+ static GPUVertFormat format = {0};
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_DEVICE_ONLY);
+ GPU_vertbuf_data_alloc(vbo, SIZE);
+ GPU_vertbuf_bind_as_ssbo(vbo, GPU_shader_get_ssbo(shader, "outputVboData"));
+
+ /* Dispatch compute task. */
+ GPU_compute_dispatch(shader, SIZE, 1, 1);
+
+ /* Check if compute has been done. */
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+
+ /* Download the vertex buffer. */
+ const float *data = static_cast<const float *>(GPU_vertbuf_read(vbo));
+ ASSERT_NE(data, nullptr);
+ for (int index = 0; index < SIZE; index++) {
+ float expected_value = index;
+ EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value);
+ }
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_vertbuf_discard(vbo);
+ GPU_shader_free(shader);
+}
+
+TEST_F(GPUTest, gpu_shader_compute_ibo)
+{
+
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ static constexpr uint SIZE = 128;
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1) in;
+
+layout(std430, binding = 1) writeonly buffer outputIboData
+{
+ uint out_indexes[];
+};
+
+void main() {
+ uint store_index = int(gl_GlobalInvocationID.x);
+ out_indexes[store_index] = store_index;
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(
+ compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo");
+ EXPECT_NE(shader, nullptr);
+ GPU_shader_bind(shader);
+
+ /* Construct IBO. */
+ GPUIndexBuf *ibo = GPU_indexbuf_build_on_device(SIZE);
+ GPU_indexbuf_bind_as_ssbo(ibo, GPU_shader_get_ssbo(shader, "outputIboData"));
+
+ /* Dispatch compute task. */
+ GPU_compute_dispatch(shader, SIZE, 1, 1);
+
+ /* Check if compute has been done. */
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+
+ /* Download the index buffer. */
+ const uint32_t *data = GPU_indexbuf_read(ibo);
+ ASSERT_NE(data, nullptr);
+ for (int index = 0; index < SIZE; index++) {
+ uint32_t expected = index;
+ EXPECT_EQ(data[index], expected);
+ }
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_indexbuf_discard(ibo);
+ GPU_shader_free(shader);
+}
+
+TEST_F(GPUTest, gpu_shader_ssbo_binding)
+{
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1) in;
+
+layout(std430, binding = 0) buffer ssboBinding0
+{
+ int data0[];
+};
+layout(std430, binding = 1) buffer ssboBinding1
+{
+ int data1[];
+};
+
+void main() {
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(compute_glsl, nullptr, nullptr, "gpu_shader_ssbo");
+ EXPECT_NE(shader, nullptr);
+ GPU_shader_bind(shader);
+
+ EXPECT_EQ(0, GPU_shader_get_ssbo(shader, "ssboBinding0"));
+ EXPECT_EQ(1, GPU_shader_get_ssbo(shader, "ssboBinding1"));
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_shader_free(shader);
+}
+
+} // namespace blender::gpu::tests
diff --git a/source/blender/gpu/tests/gpu_testing.cc b/source/blender/gpu/tests/gpu_testing.cc
index c193a19ad9c..ac42c5875c8 100644
--- a/source/blender/gpu/tests/gpu_testing.cc
+++ b/source/blender/gpu/tests/gpu_testing.cc
@@ -2,6 +2,8 @@
#include "testing/testing.h"
+#include "CLG_log.h"
+
#include "GPU_context.h"
#include "GPU_init_exit.h"
#include "gpu_testing.hh"
@@ -13,9 +15,10 @@ namespace blender::gpu {
void GPUTest::SetUp()
{
GHOST_GLSettings glSettings = {0};
+ CLG_init();
ghost_system = GHOST_CreateSystem();
ghost_context = GHOST_CreateOpenGLContext(ghost_system, glSettings);
- context = GPU_context_create(NULL);
+ context = GPU_context_create(nullptr);
GPU_init();
}
@@ -26,6 +29,7 @@ void GPUTest::TearDown()
GPU_context_discard(context);
GHOST_DisposeOpenGLContext(ghost_system, ghost_context);
GHOST_DisposeSystem(ghost_system);
+ CLG_exit();
}
} // namespace blender::gpu
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt
index 7ce795280a3..be0e364c85f 100644
--- a/source/blender/imbuf/CMakeLists.txt
+++ b/source/blender/imbuf/CMakeLists.txt
@@ -166,12 +166,6 @@ if(WITH_CODEC_FFMPEG)
${OPENJPEG_LIBRARIES}
)
add_definitions(-DWITH_FFMPEG)
-
- remove_strict_c_flags_file(
- intern/anim_movie.c
- intern/indexer.c
- intern/util.c
- )
endif()
if(WITH_IMAGE_DDS)
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index f31839751b1..53b0e295385 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -69,9 +69,9 @@ bool IMB_colormanagement_space_name_is_data(const char *name);
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]);
BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]);
-BLI_INLINE void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3]);
-BLI_INLINE void IMB_colormangement_rgb_to_xyz(float xyz[3], const float rgb[3]);
-const float *IMB_colormangement_get_xyz_to_rgb(void);
+BLI_INLINE void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3]);
+BLI_INLINE void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3]);
+const float *IMB_colormanagement_get_xyz_to_rgb(void);
/* ** Color space transformation functions ** */
void IMB_colormanagement_transform(float *buffer,
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index d131e4dacdc..69a80d6e0d3 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -70,6 +70,7 @@ extern "C" {
*/
struct ImBuf;
struct rcti;
+struct rctf;
/**
*
@@ -323,6 +324,11 @@ typedef enum IMB_Proxy_Size {
IMB_PROXY_MAX_SLOT = 4,
} IMB_Proxy_Size;
+typedef enum eIMBInterpolationFilterMode {
+ IMB_FILTER_NEAREST,
+ IMB_FILTER_BILINEAR,
+} eIMBInterpolationFilterMode;
+
/* Defaults to BL_proxy within the directory of the animation. */
void IMB_anim_set_index_dir(struct anim *anim, const char *dir);
void IMB_anim_get_fname(struct anim *anim, char *file, int size);
@@ -380,8 +386,6 @@ 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);
@@ -729,11 +733,17 @@ void IMB_processor_apply_threaded(
void(init_handle)(void *handle, int start_line, int tot_line, void *customdata),
void *(do_thread)(void *));
-typedef void (*ScanlineThreadFunc)(void *custom_data, int start_scanline, int num_scanlines);
+typedef void (*ScanlineThreadFunc)(void *custom_data, int scanline);
void IMB_processor_apply_threaded_scanlines(int total_scanlines,
ScanlineThreadFunc do_thread,
void *custom_data);
+void IMB_transform(struct ImBuf *src,
+ struct ImBuf *dst,
+ float transform_matrix[3][3],
+ struct rctf *src_crop,
+ const eIMBInterpolationFilterMode filter);
+
/* ffmpeg */
void IMB_ffmpeg_init(void);
const char *IMB_ffmpeg_last_error(void);
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index 1239d3881de..cfeffcca0ea 100644
--- a/source/blender/imbuf/intern/IMB_anim.h
+++ b/source/blender/imbuf/intern/IMB_anim.h
@@ -87,7 +87,7 @@ struct anim_index;
struct anim {
int ib_flags;
int curtype;
- int curposition; /* index 0 = 1e, 1 = 2e, enz. */
+ int cur_position; /* index 0 = 1e, 1 = 2e, enz. */
int duration_in_frames;
int frs_sec;
double frs_sec_base;
@@ -105,7 +105,6 @@ struct anim {
int orientation;
size_t framesize;
int interlacing;
- int preseek;
int streamindex;
/* avi */
@@ -132,10 +131,10 @@ struct anim {
struct SwsContext *img_convert_ctx;
int videoStream;
- struct ImBuf *last_frame;
- int64_t last_pts;
- int64_t next_pts;
- AVPacket next_packet;
+ struct ImBuf *cur_frame_final;
+ int64_t cur_pts;
+ int64_t cur_key_frame_pts;
+ AVPacket *cur_packet;
#endif
char index_dir[768];
diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h
index 363ad45e0e6..37309ccc13a 100644
--- a/source/blender/imbuf/intern/IMB_indexer.h
+++ b/source/blender/imbuf/intern/IMB_indexer.h
@@ -49,6 +49,7 @@
typedef struct anim_index_entry {
int frameno;
uint64_t seek_pos;
+ uint64_t seek_pos_pts;
uint64_t seek_pos_dts;
uint64_t pts;
} anim_index_entry;
@@ -77,14 +78,19 @@ typedef struct anim_index_builder {
} anim_index_builder;
anim_index_builder *IMB_index_builder_create(const char *name);
-void IMB_index_builder_add_entry(
- anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts);
+void IMB_index_builder_add_entry(anim_index_builder *fp,
+ int frameno,
+ uint64_t seek_pos,
+ uint64_t seek_pos_pts,
+ uint64_t seek_pos_dts,
+ uint64_t pts);
void IMB_index_builder_proc_frame(anim_index_builder *fp,
unsigned char *buffer,
int data_size,
int frameno,
uint64_t seek_pos,
+ uint64_t seek_pos_pts,
uint64_t seek_pos_dts,
uint64_t pts);
@@ -92,6 +98,7 @@ void IMB_index_builder_finish(anim_index_builder *fp, int rollback);
struct anim_index *IMB_indexer_open(const char *name);
uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index);
+uint64_t IMB_indexer_get_seek_pos_pts(struct anim_index *idx, int frame_index);
uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index);
int IMB_indexer_get_frame_index(struct anim_index *idx, int frameno);
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index d36c7bbe486..796f7469fb3 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -79,6 +79,7 @@
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
+# include <libavutil/imgutils.h>
# include <libavutil/rational.h>
# include <libswscale/swscale.h>
@@ -432,8 +433,7 @@ static int startavi(struct anim *anim)
anim->orientation = 0;
anim->framesize = anim->x * anim->y * 4;
- anim->curposition = 0;
- anim->preseek = 0;
+ anim->cur_position = 0;
# if 0
printf("x:%d y:%d size:%d interl:%d dur:%d\n",
@@ -519,12 +519,10 @@ static int startffmpeg(struct anim *anim)
double frs_den;
int streamcount;
-# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
/* The following for color space determination */
int srcRange, dstRange, brightness, contrast, saturation;
int *table;
const int *inv_table;
-# endif
if (anim == NULL) {
return (-1);
@@ -547,7 +545,7 @@ static int startffmpeg(struct anim *anim)
video_stream_index = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
- if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (streamcount > 0) {
streamcount--;
continue;
@@ -563,26 +561,38 @@ static int startffmpeg(struct anim *anim)
}
video_stream = pFormatCtx->streams[video_stream_index];
- pCodecCtx = video_stream->codec;
/* Find the decoder for the video stream */
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+ pCodec = avcodec_find_decoder(video_stream->codecpar->codec_id);
if (pCodec == NULL) {
avformat_close_input(&pFormatCtx);
return -1;
}
- pCodecCtx->workaround_bugs = 1;
+ pCodecCtx = avcodec_alloc_context3(NULL);
+ avcodec_parameters_to_context(pCodecCtx, video_stream->codecpar);
+ pCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
- pCodecCtx->thread_count = BLI_system_thread_count();
- pCodecCtx->thread_type = FF_THREAD_SLICE;
+ if (pCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ pCodecCtx->thread_count = 0;
+ }
+ else {
+ pCodecCtx->thread_count = BLI_system_thread_count();
+ }
+
+ if (pCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ pCodecCtx->thread_type = FF_THREAD_FRAME;
+ }
+ else if (pCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ pCodecCtx->thread_type = FF_THREAD_SLICE;
+ }
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
avformat_close_input(&pFormatCtx);
return -1;
}
if (pCodecCtx->pix_fmt == AV_PIX_FMT_NONE) {
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&pFormatCtx);
return -1;
}
@@ -628,7 +638,7 @@ static int startffmpeg(struct anim *anim)
anim->params = 0;
anim->x = pCodecCtx->width;
- anim->y = av_get_cropped_height_from_codec(pCodecCtx);
+ anim->y = pCodecCtx->height;
anim->pFormatCtx = pFormatCtx;
anim->pCodecCtx = pCodecCtx;
@@ -639,11 +649,12 @@ static int startffmpeg(struct anim *anim)
anim->orientation = 0;
anim->framesize = anim->x * anim->y * 4;
- anim->curposition = -1;
- anim->last_frame = 0;
- anim->last_pts = -1;
- anim->next_pts = -1;
- anim->next_packet.stream_index = -1;
+ anim->cur_position = -1;
+ anim->cur_frame_final = 0;
+ anim->cur_pts = -1;
+ anim->cur_key_frame_pts = -1;
+ anim->cur_packet = av_packet_alloc();
+ anim->cur_packet->stream_index = -1;
anim->pFrame = av_frame_alloc();
anim->pFrameComplete = false;
@@ -657,8 +668,9 @@ static int startffmpeg(struct anim *anim)
if (av_frame_get_buffer(anim->pFrameRGB, 32) < 0) {
fprintf(stderr, "Could not allocate frame data.\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -667,10 +679,11 @@ static int startffmpeg(struct anim *anim)
}
}
- if (avpicture_get_size(AV_PIX_FMT_RGBA, anim->x, anim->y) != anim->x * anim->y * 4) {
+ if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) {
fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -679,21 +692,17 @@ static int startffmpeg(struct anim *anim)
}
if (anim->ib_flags & IB_animdeinterlace) {
- avpicture_fill((AVPicture *)anim->pFrameDeinterlaced,
- MEM_callocN(avpicture_get_size(anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height),
- "ffmpeg deinterlace"),
- anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height);
- }
-
- if (pCodecCtx->has_b_frames) {
- anim->preseek = 25; /* FIXME: detect gopsize ... */
- }
- else {
- anim->preseek = 0;
+ av_image_fill_arrays(anim->pFrameDeinterlaced->data,
+ anim->pFrameDeinterlaced->linesize,
+ MEM_callocN(av_image_get_buffer_size(anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height,
+ 1),
+ "ffmpeg deinterlace"),
+ anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height,
+ 1);
}
anim->img_convert_ctx = sws_getContext(anim->x,
@@ -702,15 +711,16 @@ static int startffmpeg(struct anim *anim)
anim->x,
anim->y,
AV_PIX_FMT_RGBA,
- SWS_FAST_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT,
+ SWS_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT,
NULL,
NULL,
NULL);
if (!anim->img_convert_ctx) {
fprintf(stderr, "Can't transform color space??? Bailing out...\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -718,7 +728,6 @@ static int startffmpeg(struct anim *anim)
return -1;
}
-# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
/* Try do detect if input has 0-255 YCbCR range (JFIF Jpeg MotionJpeg) */
if (!sws_getColorspaceDetails(anim->img_convert_ctx,
(int **)&inv_table,
@@ -745,7 +754,6 @@ static int startffmpeg(struct anim *anim)
else {
fprintf(stderr, "Warning: Could not set libswscale colorspace details.\n");
}
-# endif
return 0;
}
@@ -753,13 +761,13 @@ static int startffmpeg(struct anim *anim)
/* postprocess the image in anim->pFrame and do color conversion
* and deinterlacing stuff.
*
- * Output is anim->last_frame
+ * Output is anim->cur_frame_final
*/
static void ffmpeg_postprocess(struct anim *anim)
{
AVFrame *input = anim->pFrame;
- ImBuf *ibuf = anim->last_frame;
+ ImBuf *ibuf = anim->cur_frame_final;
int filter_y = 0;
if (!anim->pFrameComplete) {
@@ -784,11 +792,11 @@ static void ffmpeg_postprocess(struct anim *anim)
input->data[3]);
if (anim->ib_flags & IB_animdeinterlace) {
- if (avpicture_deinterlace((AVPicture *)anim->pFrameDeinterlaced,
- (const AVPicture *)anim->pFrame,
- anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height) < 0) {
+ if (av_image_deinterlace(anim->pFrameDeinterlaced,
+ anim->pFrame,
+ anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height) < 0) {
filter_y = true;
}
else {
@@ -797,81 +805,87 @@ static void ffmpeg_postprocess(struct anim *anim)
}
if (!need_aligned_ffmpeg_buffer(anim)) {
- avpicture_fill((AVPicture *)anim->pFrameRGB,
- (unsigned char *)ibuf->rect,
- AV_PIX_FMT_RGBA,
- anim->x,
- anim->y);
- }
-
- if (ENDIAN_ORDER == B_ENDIAN) {
- int *dstStride = anim->pFrameRGB->linesize;
- uint8_t **dst = anim->pFrameRGB->data;
- const int dstStride2[4] = {dstStride[0], 0, 0, 0};
- uint8_t *dst2[4] = {dst[0], 0, 0, 0};
- int x, y, h, w;
- unsigned char *bottom;
- unsigned char *top;
-
- sws_scale(anim->img_convert_ctx,
- (const uint8_t *const *)input->data,
- input->linesize,
- 0,
- anim->y,
- dst2,
- dstStride2);
-
- bottom = (unsigned char *)ibuf->rect;
- top = bottom + ibuf->x * (ibuf->y - 1) * 4;
-
- h = (ibuf->y + 1) / 2;
- w = ibuf->x;
-
- for (y = 0; y < h; y++) {
- unsigned char tmp[4];
- unsigned int *tmp_l = (unsigned int *)tmp;
-
- for (x = 0; x < w; x++) {
- tmp[0] = bottom[0];
- tmp[1] = bottom[1];
- tmp[2] = bottom[2];
- tmp[3] = bottom[3];
-
- bottom[0] = top[0];
- bottom[1] = top[1];
- bottom[2] = top[2];
- bottom[3] = top[3];
-
- *(unsigned int *)top = *tmp_l;
-
- bottom += 4;
- top += 4;
- }
- top -= 8 * w;
+ av_image_fill_arrays(anim->pFrameRGB->data,
+ anim->pFrameRGB->linesize,
+ (unsigned char *)ibuf->rect,
+ AV_PIX_FMT_RGBA,
+ anim->x,
+ anim->y,
+ 1);
+ }
+
+# if defined(__x86_64__) || defined(_M_X64)
+ /* Scale and flip image over Y axis in one go, using negative strides.
+ * This doesn't work with ARM/PowerPC though and may be misusing the API.
+ * Limit it x86_64 where it appears to work.
+ * http://trac.ffmpeg.org/ticket/9060 */
+ int *dstStride = anim->pFrameRGB->linesize;
+ uint8_t **dst = anim->pFrameRGB->data;
+ const int dstStride2[4] = {-dstStride[0], 0, 0, 0};
+ uint8_t *dst2[4] = {dst[0] + (anim->y - 1) * dstStride[0], 0, 0, 0};
+
+ sws_scale(anim->img_convert_ctx,
+ (const uint8_t *const *)input->data,
+ input->linesize,
+ 0,
+ anim->y,
+ dst2,
+ dstStride2);
+# else
+ /* Scale with swscale then flip image over Y axis. */
+ int *dstStride = anim->pFrameRGB->linesize;
+ uint8_t **dst = anim->pFrameRGB->data;
+ const int dstStride2[4] = {dstStride[0], 0, 0, 0};
+ uint8_t *dst2[4] = {dst[0], 0, 0, 0};
+ int x, y, h, w;
+ unsigned char *bottom;
+ unsigned char *top;
+
+ sws_scale(anim->img_convert_ctx,
+ (const uint8_t *const *)input->data,
+ input->linesize,
+ 0,
+ anim->y,
+ dst2,
+ dstStride2);
+
+ bottom = (unsigned char *)ibuf->rect;
+ top = bottom + ibuf->x * (ibuf->y - 1) * 4;
+
+ h = (ibuf->y + 1) / 2;
+ w = ibuf->x;
+
+ for (y = 0; y < h; y++) {
+ unsigned char tmp[4];
+ unsigned int *tmp_l = (unsigned int *)tmp;
+
+ for (x = 0; x < w; x++) {
+ tmp[0] = bottom[0];
+ tmp[1] = bottom[1];
+ tmp[2] = bottom[2];
+ tmp[3] = bottom[3];
+
+ bottom[0] = top[0];
+ bottom[1] = top[1];
+ bottom[2] = top[2];
+ bottom[3] = top[3];
+
+ *(unsigned int *)top = *tmp_l;
+
+ bottom += 4;
+ top += 4;
}
+ top -= 8 * w;
}
- else {
- int *dstStride = anim->pFrameRGB->linesize;
- uint8_t **dst = anim->pFrameRGB->data;
- const int dstStride2[4] = {-dstStride[0], 0, 0, 0};
- uint8_t *dst2[4] = {dst[0] + (anim->y - 1) * dstStride[0], 0, 0, 0};
-
- sws_scale(anim->img_convert_ctx,
- (const uint8_t *const *)input->data,
- input->linesize,
- 0,
- anim->y,
- dst2,
- dstStride2);
- }
+# endif
if (need_aligned_ffmpeg_buffer(anim)) {
- uint8_t *src = anim->pFrameRGB->data[0];
- uint8_t *dst = (uint8_t *)ibuf->rect;
+ uint8_t *buf_src = anim->pFrameRGB->data[0];
+ uint8_t *buf_dst = (uint8_t *)ibuf->rect;
for (int y = 0; y < anim->y; y++) {
- memcpy(dst, src, anim->x * 4);
- dst += anim->x * 4;
- src += anim->pFrameRGB->linesize[0];
+ memcpy(buf_dst, buf_src, anim->x * 4);
+ buf_dst += anim->x * 4;
+ buf_src += anim->pFrameRGB->linesize[0];
}
}
@@ -880,7 +894,7 @@ static void ffmpeg_postprocess(struct anim *anim)
}
}
-/* decode one video frame also considering the packet read into next_packet */
+/* decode one video frame also considering the packet read into cur_packet */
static int ffmpeg_decode_video_frame(struct anim *anim)
{
@@ -888,125 +902,81 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
- if (anim->next_packet.stream_index == anim->videoStream) {
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ if (anim->cur_packet->stream_index == anim->videoStream) {
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
}
- while ((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) {
+ while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) {
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
"%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n",
- (anim->next_packet.stream_index == anim->videoStream) ? "->" : " ",
- anim->next_packet.stream_index,
+ (anim->cur_packet->stream_index == anim->videoStream) ? "->" : " ",
+ anim->cur_packet->stream_index,
anim->videoStream,
- (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.dts,
- (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.pts,
- (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
- if (anim->next_packet.stream_index == anim->videoStream) {
+ (anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->dts,
+ (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts,
+ (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
+ if (anim->cur_packet->stream_index == anim->videoStream) {
anim->pFrameComplete = 0;
- avcodec_decode_video2(
- anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet);
+ avcodec_send_packet(anim->pCodecCtx, anim->cur_packet);
+ anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
- anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
+ anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
+ if (anim->pFrame->key_frame) {
+ anim->cur_key_frame_pts = anim->cur_pts;
+ }
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- " FRAME DONE: next_pts=%" PRId64 " pkt_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
+ " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
- (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts,
- (int64_t)anim->next_pts);
+ (int64_t)anim->cur_pts);
break;
}
}
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
}
if (rval == AVERROR_EOF) {
- /* this sets size and data fields to zero,
- * which is necessary to decode the remaining data
- * in the decoder engine after EOF. It also prevents a memory
- * leak, since av_read_frame spills out a full size packet even
- * on EOF... (and: it's safe to call on NULL packets) */
-
- av_free_packet(&anim->next_packet);
-
- anim->next_packet.size = 0;
- anim->next_packet.data = 0;
-
+ /* Flush any remaining frames out of the decoder. */
anim->pFrameComplete = 0;
- avcodec_decode_video2(
- anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet);
+ avcodec_send_packet(anim->pCodecCtx, NULL);
+ anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
- anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
+ anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
+ if (anim->pFrame->key_frame) {
+ anim->cur_key_frame_pts = anim->cur_pts;
+ }
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- " FRAME DONE (after EOF): next_pts=%" PRId64 " pkt_pts=%" PRId64
- ", guessed_pts=%" PRId64 "\n",
+ " FRAME DONE (after EOF): cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
- (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts,
- (int64_t)anim->next_pts);
+ (int64_t)anim->cur_pts);
rval = 0;
}
}
if (rval < 0) {
- anim->next_packet.stream_index = -1;
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
av_log(anim->pFormatCtx,
AV_LOG_ERROR,
" DECODE READ FAILED: av_read_frame() "
- "returned error: %d\n",
- rval);
+ "returned error: %s\n",
+ av_err2str(rval));
}
return (rval >= 0);
}
-static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search)
-{
- /* there seem to exist *very* silly GOP lengths out in the wild... */
- int count = 1000;
-
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
- (int64_t)anim->next_pts,
- (int64_t)pts_to_search);
-
- while (count > 0 && anim->next_pts < pts_to_search) {
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- " WHILE: pts=%" PRId64 " in search of %" PRId64 "\n",
- (int64_t)anim->next_pts,
- (int64_t)pts_to_search);
- if (!ffmpeg_decode_video_frame(anim)) {
- break;
- }
- count--;
- }
- if (count == 0) {
- av_log(anim->pFormatCtx,
- AV_LOG_ERROR,
- "SCAN failed: completely lost in stream, "
- "bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n",
- (int64_t)anim->next_pts,
- (int64_t)pts_to_search);
- }
- if (anim->next_pts == pts_to_search) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
- }
- else {
- av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n");
- }
-}
-
static int match_format(const char *name, AVFormatContext *pFormatCtx)
{
const char *p;
@@ -1049,163 +1019,365 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
return false;
}
-static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc)
+static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position)
{
- int64_t pts_to_search = 0;
- double frame_rate;
- double pts_time_base;
- int64_t st_time;
- struct anim_index *tc_index = 0;
- AVStream *v_st;
- int new_frame_index = 0; /* To quiet gcc barking... */
- int old_frame_index = 0; /* To quiet gcc barking... */
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ int64_t st_time = anim->pFormatCtx->start_time;
+ int64_t pos = (int64_t)(position)*AV_TIME_BASE;
+ /* Step back half a time base position to make sure that we get the requested
+ * frame and not the one after it.
+ */
+ pos -= (AV_TIME_BASE / 2);
+ pos /= frame_rate;
- if (anim == NULL) {
- return 0;
- }
-
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n",
+ pos,
+ (st_time != AV_NOPTS_VALUE) ? st_time : 0);
- if (tc != IMB_TC_NONE) {
- tc_index = IMB_anim_open_index(anim, tc);
+ if (pos < 0) {
+ pos = 0;
}
- v_st = anim->pFormatCtx->streams[anim->videoStream];
+ if (st_time != AV_NOPTS_VALUE) {
+ pos += st_time;
+ }
- frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ return pos;
+}
- st_time = anim->pFormatCtx->start_time;
- pts_time_base = av_q2d(v_st->time_base);
+/* This gives us an estimate of which pts our requested frame will have.
+ * Note that this might be off a bit in certain video files, but it should still be close enough.
+ */
+static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
+ struct anim_index *tc_index,
+ int position)
+{
+ int64_t pts_to_search;
if (tc_index) {
- new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
- old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->curposition);
+ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index);
}
else {
- pts_to_search = (long long)floor(((double)position) / pts_time_base / frame_rate + 0.5);
+ int64_t st_time = anim->pFormatCtx->start_time;
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL);
+ AVRational time_base = v_st->time_base;
+
+ int64_t steps_per_frame = (frame_rate.den * time_base.den) / (frame_rate.num * time_base.num);
+ pts_to_search = position * steps_per_frame;
- if (st_time != AV_NOPTS_VALUE) {
- pts_to_search += st_time / pts_time_base / AV_TIME_BASE;
+ if (st_time != AV_NOPTS_VALUE && st_time != 0) {
+ int64_t start_frame = (double)st_time / AV_TIME_BASE * av_q2d(frame_rate);
+ pts_to_search += start_frame * steps_per_frame;
}
}
+ return pts_to_search;
+}
+
+/* Check if the pts will get us the same frame that we already have in memory from last decode. */
+static bool ffmpeg_pts_matches_last_frame(struct anim *anim, int64_t pts_to_search)
+{
+ if (anim->pFrame && anim->cur_frame_final) {
+ int64_t diff = pts_to_search - anim->cur_pts;
+ return diff >= 0 && diff < anim->pFrame->pkt_duration;
+ }
+
+ return false;
+}
+
+static bool ffmpeg_is_first_frame_decode(struct anim *anim, int position)
+{
+ return position == 0 && anim->cur_position == -1;
+}
+
+/* Decode frames one by one until its PTS matches pts_to_search. */
+static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search)
+{
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within current GOP\n");
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
- ")\n",
- (int64_t)pts_to_search,
- pts_time_base,
- frame_rate,
- st_time);
+ "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
+ (int64_t)anim->cur_pts,
+ (int64_t)pts_to_search);
+
+ int64_t start_gop_frame = anim->cur_key_frame_pts;
+ bool scan_fuzzy = false;
- if (anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search) {
+ while (anim->cur_pts < pts_to_search) {
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- "FETCH: frame repeat: last: %" PRId64 " next: %" PRId64 "\n",
- (int64_t)anim->last_pts,
- (int64_t)anim->next_pts);
- IMB_refImBuf(anim->last_frame);
- anim->curposition = position;
- return anim->last_frame;
- }
+ " WHILE: pts=%" PRId64 " in search of %" PRId64 "\n",
+ (int64_t)anim->cur_pts,
+ (int64_t)pts_to_search);
+ if (!ffmpeg_decode_video_frame(anim)) {
+ break;
+ }
- if (position > anim->curposition + 1 && anim->preseek && !tc_index &&
- position - (anim->curposition + 1) < anim->preseek) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within preseek interval (no index)\n");
+ if (start_gop_frame != anim->cur_key_frame_pts) {
+ break;
+ }
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ if (anim->cur_pts < pts_to_search &&
+ anim->cur_pts + anim->pFrame->pkt_duration > pts_to_search) {
+ /* Our estimate of the pts was a bit off, but we have the frame we want. */
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN fuzzy frame match\n");
+ scan_fuzzy = true;
+ break;
+ }
}
- else if (tc_index && IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) {
+
+ if (start_gop_frame != anim->cur_key_frame_pts) {
+ /* We went into an other GOP frame. This should never happen as we should have positioned us
+ * correctly by seeking into the GOP frame that contains the frame we want. */
av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "FETCH: within preseek interval "
- "(index tells us)\n");
+ AV_LOG_ERROR,
+ "SCAN failed: completely lost in stream, "
+ "bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n",
+ (int64_t)anim->cur_pts,
+ (int64_t)pts_to_search);
+ }
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ if (scan_fuzzy || anim->cur_pts == pts_to_search) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
}
- else if (position != anim->curposition + 1) {
- int64_t pos;
- int ret;
+ else {
+ av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n");
+ }
+}
- if (tc_index) {
- uint64_t dts;
+/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or
+ * read_seek2() functions defined. When seeking in these formats, rule to seek to last
+ * necessary I-frame is not honored. It is not even guaranteed that I-frame, that must be
+ * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and
+ * https://developer.blender.org/T86944. */
+static int ffmpeg_generic_seek_workaround(struct anim *anim,
+ int64_t *requested_pos,
+ int64_t pts_to_search)
+{
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ int64_t current_pos = *requested_pos;
+ int64_t offset = 0;
- pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
- dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
+ int64_t cur_pts, prev_pts = -1;
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos);
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
+ /* Step backward frame by frame until we find the key frame we are looking for. */
+ while (current_pos != 0) {
+ current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
+ current_pos = max_ii(current_pos, 0);
- if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
+ /* Seek to timestamp. */
+ if (av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD) < 0) {
+ break;
+ }
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE);
- av_update_cur_dts(anim->pFormatCtx, v_st, dts);
- }
- else {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using DTS pos\n");
- ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, dts, AVSEEK_FLAG_BACKWARD);
+ /* Read first video stream packet. */
+ AVPacket *read_packet = av_packet_alloc();
+ while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) {
+ if (read_packet->stream_index == anim->videoStream) {
+ break;
}
+ av_packet_unref(read_packet);
}
- else {
- pos = (int64_t)position * AV_TIME_BASE / frame_rate;
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n",
- pos,
- (st_time != AV_NOPTS_VALUE) ? st_time : 0);
+ /* If this packet contains an I-frame, this could be the frame that we need. */
+ bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY;
+ /* We need to check the packet timestamp as the key frame could be for a GOP forward in the the
+ * video stream. So if it has a larger timestamp than the frame we want, ignore it.
+ */
+ cur_pts = timestamp_from_pts_or_dts(read_packet->pts, read_packet->dts);
+ av_packet_free(&read_packet);
- if (pos < 0) {
- pos = 0;
+ if (is_key_frame) {
+ if (cur_pts <= pts_to_search) {
+ /* We found the I-frame we were looking for! */
+ break;
}
-
- if (st_time != AV_NOPTS_VALUE) {
- pos += st_time;
+ if (cur_pts == prev_pts) {
+ /* We got the same key frame packet twice.
+ * This probably means that we have hit the beginning of the stream. */
+ break;
}
+ }
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
+ prev_pts = cur_pts;
+ offset++;
+ }
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
- }
+ *requested_pos = current_pos;
- if (ret < 0) {
- av_log(anim->pFormatCtx,
- AV_LOG_ERROR,
- "FETCH: "
- "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
- "): errcode = %d\n",
- pos,
- position,
- (int64_t)pts_to_search,
- ret);
+ /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */
+ return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD);
+}
+
+/* Seek to last necessary key frame. */
+static int ffmpeg_seek_to_key_frame(struct anim *anim,
+ int position,
+ struct anim_index *tc_index,
+ int64_t pts_to_search)
+{
+ int64_t pos;
+ int ret;
+
+ if (tc_index) {
+ /* We can use timestamps generated from our indexer to seek. */
+ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
+ int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->cur_position);
+
+ if (IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) {
+ /* No need to seek, return early. */
+ return 0;
}
+ uint64_t pts;
+ uint64_t dts;
+
+ pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
+ pts = IMB_indexer_get_seek_pos_pts(tc_index, new_frame_index);
+ dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
+
+ anim->cur_key_frame_pts = timestamp_from_pts_or_dts(pts, dts);
- avcodec_flush_buffers(anim->pCodecCtx);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pts = %" PRIu64 "\n", pts);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
- anim->next_pts = -1;
+ if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
- if (anim->next_packet.stream_index == anim->videoStream) {
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE);
}
+ else {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS pos\n");
+ ret = av_seek_frame(
+ anim->pFormatCtx, anim->videoStream, anim->cur_key_frame_pts, AVSEEK_FLAG_BACKWARD);
+ }
+ }
+ else {
+ /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from.
+ */
+ pos = ffmpeg_get_seek_pos(anim, position);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
+
+ AVFormatContext *format_ctx = anim->pFormatCtx;
- /* memset(anim->pFrame, ...) ?? */
+ if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) {
+ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ }
+ else {
+ ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos);
+ }
if (ret >= 0) {
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ /* Double check if we need to seek and decode all packets. */
+ AVPacket *current_gop_start_packet = av_packet_alloc();
+ while (av_read_frame(anim->pFormatCtx, current_gop_start_packet) >= 0) {
+ if (current_gop_start_packet->stream_index == anim->videoStream) {
+ break;
+ }
+ av_packet_unref(current_gop_start_packet);
+ }
+ int64_t gop_pts = timestamp_from_pts_or_dts(current_gop_start_packet->pts,
+ current_gop_start_packet->dts);
+
+ av_packet_free(&current_gop_start_packet);
+ bool same_gop = gop_pts == anim->cur_key_frame_pts;
+
+ if (same_gop && position > anim->cur_position) {
+ /* Change back to our old frame position so we can simply continue decoding from there. */
+ AVPacket *temp = av_packet_alloc();
+ while (av_read_frame(anim->pFormatCtx, temp) >= 0) {
+ int64_t temp_pts = timestamp_from_pts_or_dts(temp->pts, temp->dts);
+ int64_t cur_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts,
+ anim->cur_packet->dts);
+ if (temp->stream_index == anim->videoStream && temp_pts == cur_pts) {
+ break;
+ }
+ av_packet_unref(temp);
+ }
+ av_packet_free(&temp);
+ return 0;
+ }
+
+ anim->cur_key_frame_pts = gop_pts;
+ /* Seek back so we are at the correct position after we decoded a frame. */
+ av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
}
}
- else if (position == 0 && anim->curposition == -1) {
- /* first frame without seeking special case... */
- ffmpeg_decode_video_frame(anim);
+
+ if (ret < 0) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_ERROR,
+ "FETCH: "
+ "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
+ "): errcode = %d\n",
+ pos,
+ position,
+ pts_to_search,
+ ret);
}
- else {
+ /* Flush the internal buffers of ffmpeg. This needs to be done after seeking to avoid decoding
+ * errors. */
+ avcodec_flush_buffers(anim->pCodecCtx);
+
+ anim->cur_pts = -1;
+
+ if (anim->cur_packet->stream_index == anim->videoStream) {
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
+ }
+
+ return ret;
+}
+
+static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc)
+{
+ if (anim == NULL) {
+ return NULL;
+ }
+
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
+
+ struct anim_index *tc_index = IMB_anim_open_index(anim, tc);
+ int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ double pts_time_base = av_q2d(v_st->time_base);
+ int64_t st_time = anim->pFormatCtx->start_time;
+
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
+ ")\n",
+ (int64_t)pts_to_search,
+ pts_time_base,
+ frame_rate,
+ st_time);
+
+ if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "FETCH: frame repeat: pts: %" PRId64 "\n",
+ (int64_t)anim->cur_pts);
+ IMB_refImBuf(anim->cur_frame_final);
+ anim->cur_position = position;
+ return anim->cur_frame_final;
+ }
+
+ if (position == anim->cur_position + 1 || ffmpeg_is_first_frame_decode(anim, position)) {
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n");
+ ffmpeg_decode_video_frame(anim);
+ }
+ else if (ffmpeg_seek_to_key_frame(anim, position, tc_index, pts_to_search) >= 0) {
+ ffmpeg_decode_video_frame_scan(anim, pts_to_search);
}
- IMB_freeImBuf(anim->last_frame);
+ IMB_freeImBuf(anim->cur_frame_final);
/* Certain versions of FFmpeg have a bug in libswscale which ends up in crash
* when destination buffer is not properly aligned. For example, this happens
@@ -1225,23 +1397,20 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
*
* The issue was reported to FFmpeg under ticket #8747 in the FFmpeg tracker
* and is fixed in the newer versions than 4.3.1. */
- anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, 0);
- anim->last_frame->rect = MEM_mallocN_aligned((size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf");
- anim->last_frame->mall |= IB_rect;
+ anim->cur_frame_final = IMB_allocImBuf(anim->x, anim->y, 32, 0);
+ anim->cur_frame_final->rect = MEM_mallocN_aligned(
+ (size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf");
+ anim->cur_frame_final->mall |= IB_rect;
- anim->last_frame->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace);
+ anim->cur_frame_final->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace);
ffmpeg_postprocess(anim);
- anim->last_pts = anim->next_pts;
-
- ffmpeg_decode_video_frame(anim);
-
- anim->curposition = position;
+ anim->cur_position = position;
- IMB_refImBuf(anim->last_frame);
+ IMB_refImBuf(anim->cur_frame_final);
- return anim->last_frame;
+ return anim->cur_frame_final;
}
static void free_anim_ffmpeg(struct anim *anim)
@@ -1251,32 +1420,30 @@ static void free_anim_ffmpeg(struct anim *anim)
}
if (anim->pCodecCtx) {
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
- /* Special case here: pFrame could share pointers with codec,
- * so in order to avoid double-free we don't use av_frame_free()
- * to free the frame.
- *
- * Could it be a bug in FFmpeg?
- */
- av_free(anim->pFrame);
+ av_frame_free(&anim->pFrame);
if (!need_aligned_ffmpeg_buffer(anim)) {
/* If there's no need for own aligned buffer it means that FFmpeg's
* frame shares the same buffer as temporary ImBuf. In this case we
* should not free the buffer when freeing the FFmpeg buffer.
*/
- avpicture_fill((AVPicture *)anim->pFrameRGB, NULL, AV_PIX_FMT_RGBA, anim->x, anim->y);
+ av_image_fill_arrays(anim->pFrameRGB->data,
+ anim->pFrameRGB->linesize,
+ NULL,
+ AV_PIX_FMT_RGBA,
+ anim->x,
+ anim->y,
+ 1);
}
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
sws_freeContext(anim->img_convert_ctx);
- IMB_freeImBuf(anim->last_frame);
- if (anim->next_packet.stream_index != -1) {
- av_free_packet(&anim->next_packet);
- }
+ IMB_freeImBuf(anim->cur_frame_final);
}
anim->duration_in_frames = 0;
}
@@ -1410,13 +1577,13 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
an_stringenc(anim->name, head, tail, digits, pic);
ibuf = IMB_loadiffname(anim->name, IB_rect, anim->colorspace);
if (ibuf) {
- anim->curposition = position;
+ anim->cur_position = position;
}
break;
case ANIM_MOVIE:
ibuf = movie_fetchibuf(anim, position);
if (ibuf) {
- anim->curposition = position;
+ anim->cur_position = position;
IMB_convert_rgba_to_abgr(ibuf);
}
break;
@@ -1424,7 +1591,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
case ANIM_AVI:
ibuf = avi_fetchibuf(anim, position);
if (ibuf) {
- anim->curposition = position;
+ anim->cur_position = position;
}
break;
#endif
@@ -1432,7 +1599,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
case ANIM_FFMPEG:
ibuf = ffmpeg_fetchibuf(anim, position, tc);
if (ibuf) {
- anim->curposition = position;
+ anim->cur_position = position;
}
filter_y = 0; /* done internally */
break;
@@ -1443,7 +1610,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
if (filter_y) {
IMB_filtery(ibuf);
}
- BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->curposition + 1);
+ BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->cur_position + 1);
}
return ibuf;
}
@@ -1498,16 +1665,6 @@ bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bo
return false;
}
-void IMB_anim_set_preseek(struct anim *anim, int preseek)
-{
- anim->preseek = preseek;
-}
-
-int IMB_anim_get_preseek(struct anim *anim)
-{
- return anim->preseek;
-}
-
int IMB_anim_get_image_width(struct anim *anim)
{
return anim->x;
diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c
index a5c558fc216..ad72f373d12 100644
--- a/source/blender/imbuf/intern/bmp.c
+++ b/source/blender/imbuf/intern/bmp.c
@@ -74,7 +74,7 @@ typedef struct BMPHEADER {
static bool checkbmp(const uchar *mem, const size_t size)
{
- if (size < BMP_FILEHEADER_SIZE) {
+ if (size < (BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER))) {
return false;
}
@@ -113,56 +113,57 @@ bool imb_is_a_bmp(const uchar *buf, size_t size)
static size_t imb_bmp_calc_row_size_in_bytes(size_t x, size_t depth)
{
- if (depth <= 8) {
- return (depth * x + 31) / 32 * 4;
- }
- return (depth >> 3) * x;
+ /* https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader#calculating-surface-stride
+ */
+ return (((x * depth) + 31) & ~31) >> 3;
}
ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImBuf *ibuf = NULL;
BMPINFOHEADER bmi;
- int x, y, depth, ibuf_depth, skip;
+ int ibuf_depth;
const uchar *bmp;
uchar *rect;
ushort col;
- double xppm, yppm;
bool top_to_bottom = false;
- (void)size; /* unused */
-
if (checkbmp(mem, size) == 0) {
return NULL;
}
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
- const size_t pixel_data_offset = LITTLE_LONG(*(int *)(mem + 10));
- bmp = mem + pixel_data_offset;
+ /* For systems where an int needs to be 4 bytes aligned. */
+ memcpy(&bmi, mem + BMP_FILEHEADER_SIZE, sizeof(bmi));
+
+ const size_t palette_offset = (size_t)BMP_FILEHEADER_SIZE + LITTLE_LONG(bmi.biSize);
+ const int depth = LITTLE_SHORT(bmi.biBitCount);
+ const int xppm = LITTLE_LONG(bmi.biXPelsPerMeter);
+ const int yppm = LITTLE_LONG(bmi.biYPelsPerMeter);
+ const int x = LITTLE_LONG(bmi.biWidth);
+ int y = LITTLE_LONG(bmi.biHeight);
- if (CHECK_HEADER_FIELD_BMP(mem)) {
- /* skip fileheader */
- mem += BMP_FILEHEADER_SIZE;
+ /* Negative height means bitmap is stored top-to-bottom. */
+ if (y < 0) {
+ y = -y;
+ top_to_bottom = true;
}
- else {
+
+ /* Validate and cross-check offsets and sizes. */
+ if (x < 1 ||
+ !(depth == 1 || depth == 4 || depth == 8 || depth == 16 || depth == 24 || depth == 32)) {
return NULL;
}
- /* for systems where an int needs to be 4 bytes aligned */
- memcpy(&bmi, mem, sizeof(bmi));
-
- skip = LITTLE_LONG(bmi.biSize);
- x = LITTLE_LONG(bmi.biWidth);
- y = LITTLE_LONG(bmi.biHeight);
- depth = LITTLE_SHORT(bmi.biBitCount);
- xppm = LITTLE_LONG(bmi.biXPelsPerMeter);
- yppm = LITTLE_LONG(bmi.biYPelsPerMeter);
-
+ const size_t pixel_data_offset = (size_t)LITTLE_LONG(*(int *)(mem + 10));
+ const size_t header_bytes = BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER);
+ const size_t num_actual_data_bytes = size - pixel_data_offset;
const size_t row_size_in_bytes = imb_bmp_calc_row_size_in_bytes(x, depth);
const size_t num_expected_data_bytes = row_size_in_bytes * y;
- const size_t num_actual_data_bytes = size - pixel_data_offset;
- if (num_actual_data_bytes < num_expected_data_bytes) {
+ if (num_actual_data_bytes < num_expected_data_bytes || num_actual_data_bytes > size ||
+ pixel_data_offset < header_bytes || pixel_data_offset > (size - num_expected_data_bytes) ||
+ palette_offset < header_bytes || palette_offset > pixel_data_offset) {
return NULL;
}
@@ -173,14 +174,10 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[
ibuf_depth = depth;
}
- if (y < 0) {
- /* Negative height means bitmap is stored top-to-bottom... */
- y = -y;
- top_to_bottom = true;
- }
+ bmp = mem + pixel_data_offset;
#if 0
- printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, depth, bmi.biBitCount);
+ printf("palette_offset: %d, x: %d y: %d, depth: %d\n", palette_offset, x, y, depth);
#endif
if (flags & IB_test) {
@@ -195,7 +192,7 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[
rect = (uchar *)ibuf->rect;
if (depth <= 8) {
- const char(*palette)[4] = (void *)(mem + skip);
+ const char(*palette)[4] = (const char(*)[4])(mem + palette_offset);
const int startmask = ((1 << depth) - 1) << 8;
for (size_t i = y; i > 0; i--) {
int index;
diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.c b/source/blender/imbuf/intern/cineon/cineon_dpx.c
index de54e6dab9d..91d7b9a8b9e 100644
--- a/source/blender/imbuf/intern/cineon/cineon_dpx.c
+++ b/source/blender/imbuf/intern/cineon/cineon_dpx.c
@@ -198,10 +198,10 @@ ImBuf *imb_load_cineon(const unsigned char *mem,
int flags,
char colorspace[IM_MAX_SPACE])
{
- if (imb_is_a_cineon(mem, size)) {
- return imb_load_dpx_cineon(mem, size, 1, flags, colorspace);
+ if (!imb_is_a_cineon(mem, size)) {
+ return NULL;
}
- return NULL;
+ return imb_load_dpx_cineon(mem, size, 1, flags, colorspace);
}
bool imb_save_dpx(struct ImBuf *buf, const char *filepath, int flags)
@@ -219,8 +219,8 @@ ImBuf *imb_load_dpx(const unsigned char *mem,
int flags,
char colorspace[IM_MAX_SPACE])
{
- if (imb_is_a_dpx(mem, size)) {
- return imb_load_dpx_cineon(mem, size, 0, flags, colorspace);
+ if (!imb_is_a_dpx(mem, size)) {
+ return NULL;
}
- return NULL;
+ return imb_load_dpx_cineon(mem, size, 0, flags, colorspace);
}
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index fc0b99a82fa..71e513fb405 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -1409,7 +1409,7 @@ bool IMB_colormanagement_space_name_is_data(const char *name)
return (colorspace && colorspace->is_data);
}
-const float *IMB_colormangement_get_xyz_to_rgb()
+const float *IMB_colormanagement_get_xyz_to_rgb()
{
return &imbuf_xyz_to_rgb[0][0];
}
@@ -3539,12 +3539,11 @@ typedef struct PartialThreadData {
int xmin, ymin, xmax;
} PartialThreadData;
-static void partial_buffer_update_rect_thread_do(void *data_v,
- int start_scanline,
- int num_scanlines)
+static void partial_buffer_update_rect_thread_do(void *data_v, int scanline)
{
PartialThreadData *data = (PartialThreadData *)data_v;
- int ymin = data->ymin + start_scanline;
+ int ymin = data->ymin + scanline;
+ const int num_scanlines = 1;
partial_buffer_update_rect(data->ibuf,
data->display_buffer,
data->linear_buffer,
diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c
index e93fdd51040..c304ad8d8e5 100644
--- a/source/blender/imbuf/intern/colormanagement_inline.c
+++ b/source/blender/imbuf/intern/colormanagement_inline.c
@@ -55,12 +55,12 @@ unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3])
return unit_float_to_uchar_clamp(val);
}
-void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3])
+void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3])
{
mul_v3_m3v3(rgb, imbuf_xyz_to_rgb, xyz);
}
-void IMB_colormangement_rgb_to_xyz(float xyz[3], const float rgb[3])
+void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3])
{
mul_v3_m3v3(xyz, imbuf_rgb_to_xyz, rgb);
}
diff --git a/source/blender/imbuf/intern/dds/ColorBlock.cpp b/source/blender/imbuf/intern/dds/ColorBlock.cpp
index 3b1b970fb3c..a05c7e2a70d 100644
--- a/source/blender/imbuf/intern/dds/ColorBlock.cpp
+++ b/source/blender/imbuf/intern/dds/ColorBlock.cpp
@@ -46,11 +46,6 @@ inline static uint colorDistance(Color32 c0, Color32 c1)
}
#endif
-/** Default constructor. */
-ColorBlock::ColorBlock()
-{
-}
-
/** Init the color block from an array of colors. */
ColorBlock::ColorBlock(const uint *linearImage)
{
diff --git a/source/blender/imbuf/intern/dds/ColorBlock.h b/source/blender/imbuf/intern/dds/ColorBlock.h
index 9c3d73bbb0d..158695cfbf3 100644
--- a/source/blender/imbuf/intern/dds/ColorBlock.h
+++ b/source/blender/imbuf/intern/dds/ColorBlock.h
@@ -34,7 +34,7 @@
/** Uncompressed 4x4 color block. */
struct ColorBlock {
- ColorBlock();
+ ColorBlock() = default;
ColorBlock(const uint *linearImage);
ColorBlock(const ColorBlock &block);
ColorBlock(const Image *img, uint x, uint y);
diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
index 2a36946df8f..b665996b18f 100644
--- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
+++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
@@ -890,10 +890,6 @@ DirectDrawSurface::DirectDrawSurface(unsigned char *mem, uint size) : stream(mem
}
}
-DirectDrawSurface::~DirectDrawSurface()
-{
-}
-
bool DirectDrawSurface::isValid() const
{
if (header.fourcc != FOURCC_DDS || header.size != 124) {
diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.h b/source/blender/imbuf/intern/dds/DirectDrawSurface.h
index b6c2d1962e2..b63d705dadf 100644
--- a/source/blender/imbuf/intern/dds/DirectDrawSurface.h
+++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.h
@@ -136,7 +136,6 @@ struct DDSHeader {
class DirectDrawSurface {
public:
DirectDrawSurface(unsigned char *mem, uint size);
- ~DirectDrawSurface();
bool isValid() const;
bool isSupported() const;
diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c
index 5f580449e12..47712456014 100644
--- a/source/blender/imbuf/intern/divers.c
+++ b/source/blender/imbuf/intern/divers.c
@@ -536,13 +536,12 @@ typedef struct FloatToFloatThreadData {
int stride_from;
} FloatToFloatThreadData;
-static void imb_buffer_float_from_float_thread_do(void *data_v,
- int start_scanline,
- int num_scanlines)
+static void imb_buffer_float_from_float_thread_do(void *data_v, int scanline)
{
+ const int num_scanlines = 1;
FloatToFloatThreadData *data = (FloatToFloatThreadData *)data_v;
- size_t offset_from = ((size_t)start_scanline) * data->stride_from * data->channels_from;
- size_t offset_to = ((size_t)start_scanline) * data->stride_to * data->channels_from;
+ size_t offset_from = ((size_t)scanline) * data->stride_from * data->channels_from;
+ size_t offset_to = ((size_t)scanline) * data->stride_to * data->channels_from;
IMB_buffer_float_from_float(data->rect_to + offset_to,
data->rect_from + offset_from,
data->channels_from,
diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c
index 7ada0130059..a9b6e2bbb88 100644
--- a/source/blender/imbuf/intern/imageprocess.c
+++ b/source/blender/imbuf/intern/imageprocess.c
@@ -127,6 +127,22 @@ void bicubic_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in
/** \name Bi-Linear Interpolation
* \{ */
+BLI_INLINE void bilinear_interpolation_color_fl(
+ struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v)
+{
+ BLI_assert(outF);
+ BLI_assert(in->rect_float);
+ BLI_bilinear_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v);
+}
+
+BLI_INLINE void bilinear_interpolation_color_char(
+ struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v)
+{
+ BLI_assert(outI);
+ BLI_assert(in->rect);
+ BLI_bilinear_interpolation_char((unsigned char *)in->rect, outI, in->x, in->y, 4, u, v);
+}
+
void bilinear_interpolation_color(
struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
{
@@ -238,60 +254,58 @@ void bilinear_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, i
/** \name Nearest Interpolation
* \{ */
-/* function assumes out to be zero'ed, only does RGBA */
-void nearest_interpolation_color(
- struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+/* functions assumes out to be zero'ed, only does RGBA */
+BLI_INLINE void nearest_interpolation_color_char(
+ struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v)
{
- const float *dataF;
- unsigned char *dataI;
- int y1, x1;
-
+ BLI_assert(outI);
+ BLI_assert(in->rect);
/* ImBuf in must have a valid rect or rect_float, assume this is already checked */
+ int x1 = (int)(u);
+ int y1 = (int)(v);
- x1 = (int)(u);
- y1 = (int)(v);
+ /* sample area entirely outside image? */
+ if (x1 < 0 || x1 >= in->x || y1 < 0 || y1 >= in->y) {
+ outI[0] = outI[1] = outI[2] = outI[3] = 0;
+ return;
+ }
+
+ const size_t offset = (in->x * y1 + x1) * 4;
+ const unsigned char *dataI = (unsigned char *)in->rect + offset;
+ outI[0] = dataI[0];
+ outI[1] = dataI[1];
+ outI[2] = dataI[2];
+ outI[3] = dataI[3];
+}
+
+BLI_INLINE void nearest_interpolation_color_fl(
+ struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v)
+{
+ BLI_assert(outF);
+ BLI_assert(in->rect_float);
+ /* ImBuf in must have a valid rect or rect_float, assume this is already checked */
+ int x1 = (int)(u);
+ int y1 = (int)(v);
/* sample area entirely outside image? */
- if (x1 < 0 || x1 > in->x - 1 || y1 < 0 || y1 > in->y - 1) {
- if (outI) {
- outI[0] = outI[1] = outI[2] = outI[3] = 0;
- }
- if (outF) {
- outF[0] = outF[1] = outF[2] = outF[3] = 0.0f;
- }
+ if (x1 < 0 || x1 >= in->x || y1 < 0 || y1 >= in->y) {
+ zero_v4(outF);
return;
}
- /* sample including outside of edges of image */
- if (x1 < 0 || y1 < 0) {
- if (outI) {
- outI[0] = 0;
- outI[1] = 0;
- outI[2] = 0;
- outI[3] = 0;
- }
- if (outF) {
- outF[0] = 0.0f;
- outF[1] = 0.0f;
- outF[2] = 0.0f;
- outF[3] = 0.0f;
- }
+ const size_t offset = (in->x * y1 + x1) * 4;
+ const float *dataF = in->rect_float + offset;
+ copy_v4_v4(outF, dataF);
+}
+
+void nearest_interpolation_color(
+ struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+{
+ if (outF) {
+ nearest_interpolation_color_fl(in, outI, outF, u, v);
}
else {
- dataI = (unsigned char *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x1;
- if (outI) {
- outI[0] = dataI[0];
- outI[1] = dataI[1];
- outI[2] = dataI[2];
- outI[3] = dataI[3];
- }
- dataF = in->rect_float + ((size_t)in->x) * y1 * 4 + 4 * x1;
- if (outF) {
- outF[0] = dataF[0];
- outF[1] = dataF[1];
- outF[2] = dataF[2];
- outF[3] = dataF[3];
- }
+ nearest_interpolation_color_char(in, outI, outF, u, v);
}
}
@@ -350,6 +364,141 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in
}
/* -------------------------------------------------------------------- */
+/** \name Image transform
+ * \{ */
+typedef struct TransformUserData {
+ ImBuf *src;
+ ImBuf *dst;
+ float start_uv[2];
+ float add_x[2];
+ float add_y[2];
+ rctf src_crop;
+} TransformUserData;
+
+static void imb_transform_calc_start_uv(const float transform_matrix[3][3], float r_start_uv[2])
+{
+ float orig[2];
+ orig[0] = 0.0f;
+ orig[1] = 0.0f;
+ mul_v2_m3v2(r_start_uv, transform_matrix, orig);
+}
+
+static void imb_transform_calc_add_x(const float transform_matrix[3][3],
+ const float start_uv[2],
+ const int width,
+ float r_add_x[2])
+{
+ float uv_max_x[2];
+ uv_max_x[0] = width;
+ uv_max_x[1] = 0.0f;
+ mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x);
+ sub_v2_v2(r_add_x, start_uv);
+ mul_v2_fl(r_add_x, 1.0f / width);
+}
+
+static void imb_transform_calc_add_y(const float transform_matrix[3][3],
+ const float start_uv[2],
+ const int height,
+ float r_add_y[2])
+{
+ float uv_max_y[2];
+ uv_max_y[0] = 0.0f;
+ uv_max_y[1] = height;
+ mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y);
+ sub_v2_v2(r_add_y, start_uv);
+ mul_v2_fl(r_add_y, 1.0f / height);
+}
+
+typedef void (*InterpolationColorFunction)(
+ struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
+BLI_INLINE void imb_transform_scanlines(const TransformUserData *user_data,
+ int scanline,
+ InterpolationColorFunction interpolation)
+{
+ const int width = user_data->dst->x;
+
+ float uv[2];
+ madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline);
+
+ unsigned char *outI = NULL;
+ float *outF = NULL;
+ pixel_from_buffer(user_data->dst, &outI, &outF, 0, scanline);
+
+ for (int xi = 0; xi < width; xi++) {
+ if (uv[0] >= user_data->src_crop.xmin && uv[0] < user_data->src_crop.xmax &&
+ uv[1] >= user_data->src_crop.ymin && uv[1] < user_data->src_crop.ymax) {
+ interpolation(user_data->src, outI, outF, uv[0], uv[1]);
+ }
+ add_v2_v2(uv, user_data->add_x);
+ if (outI) {
+ outI += 4;
+ }
+ if (outF) {
+ outF += 4;
+ }
+ }
+}
+
+static void imb_transform_nearest_scanlines(void *custom_data, int scanline)
+{
+ const TransformUserData *user_data = custom_data;
+ InterpolationColorFunction interpolation = NULL;
+ if (user_data->dst->rect_float) {
+ interpolation = nearest_interpolation_color_fl;
+ }
+ else {
+ interpolation = nearest_interpolation_color_char;
+ }
+ imb_transform_scanlines(user_data, scanline, interpolation);
+}
+
+static void imb_transform_bilinear_scanlines(void *custom_data, int scanline)
+{
+ const TransformUserData *user_data = custom_data;
+ InterpolationColorFunction interpolation = NULL;
+ if (user_data->dst->rect_float) {
+ interpolation = bilinear_interpolation_color_fl;
+ }
+ else if (user_data->dst->rect) {
+ interpolation = bilinear_interpolation_color_char;
+ }
+ imb_transform_scanlines(user_data, scanline, interpolation);
+}
+
+static ScanlineThreadFunc imb_transform_scanline_func(const eIMBInterpolationFilterMode filter)
+{
+ ScanlineThreadFunc scanline_func = NULL;
+ switch (filter) {
+ case IMB_FILTER_NEAREST:
+ scanline_func = imb_transform_nearest_scanlines;
+ break;
+ case IMB_FILTER_BILINEAR:
+ scanline_func = imb_transform_bilinear_scanlines;
+ break;
+ }
+ return scanline_func;
+}
+
+void IMB_transform(struct ImBuf *src,
+ struct ImBuf *dst,
+ float transform_matrix[3][3],
+ struct rctf *src_crop,
+ const eIMBInterpolationFilterMode filter)
+{
+ TransformUserData user_data;
+ user_data.src = src;
+ user_data.dst = dst;
+ user_data.src_crop = *src_crop;
+ imb_transform_calc_start_uv(transform_matrix, user_data.start_uv);
+ imb_transform_calc_add_x(transform_matrix, user_data.start_uv, src->x, user_data.add_x);
+ imb_transform_calc_add_y(transform_matrix, user_data.start_uv, src->y, user_data.add_y);
+ ScanlineThreadFunc scanline_func = imb_transform_scanline_func(filter);
+ IMB_processor_apply_threaded_scanlines(dst->y, scanline_func, &user_data);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Threaded Image Processing
* \{ */
@@ -374,7 +523,7 @@ void IMB_processor_apply_threaded(
int total_tasks = (buffer_lines + lines_per_task - 1) / lines_per_task;
int i, start_line;
- task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
handles = MEM_callocN(handle_size * total_tasks, "processor apply threaded handles");
@@ -409,41 +558,28 @@ void IMB_processor_apply_threaded(
typedef struct ScanlineGlobalData {
void *custom_data;
ScanlineThreadFunc do_thread;
- int scanlines_per_task;
- int total_scanlines;
} ScanlineGlobalData;
-static void processor_apply_scanline_func(TaskPool *__restrict pool, void *taskdata)
+static void processor_apply_parallel(void *__restrict userdata,
+ const int scanline,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
- ScanlineGlobalData *data = BLI_task_pool_user_data(pool);
- int start_scanline = POINTER_AS_INT(taskdata);
- int num_scanlines = min_ii(data->scanlines_per_task, data->total_scanlines - start_scanline);
- data->do_thread(data->custom_data, start_scanline, num_scanlines);
+ ScanlineGlobalData *data = userdata;
+ data->do_thread(data->custom_data, scanline);
}
void IMB_processor_apply_threaded_scanlines(int total_scanlines,
ScanlineThreadFunc do_thread,
void *custom_data)
{
- const int scanlines_per_task = 64;
- ScanlineGlobalData data;
- data.custom_data = custom_data;
- data.do_thread = do_thread;
- data.scanlines_per_task = scanlines_per_task;
- data.total_scanlines = total_scanlines;
- const int total_tasks = (total_scanlines + scanlines_per_task - 1) / scanlines_per_task;
- TaskPool *task_pool = BLI_task_pool_create(&data, TASK_PRIORITY_LOW);
- for (int i = 0, start_line = 0; i < total_tasks; i++) {
- BLI_task_pool_push(
- task_pool, processor_apply_scanline_func, POINTER_FROM_INT(start_line), false, NULL);
- start_line += scanlines_per_task;
- }
-
- /* work and wait until tasks are done */
- BLI_task_pool_work_and_wait(task_pool);
-
- /* Free memory. */
- BLI_task_pool_free(task_pool);
+ TaskParallelSettings settings;
+ ScanlineGlobalData data = {
+ .do_thread = do_thread,
+ .custom_data = custom_data,
+ };
+
+ BLI_parallel_range_settings_defaults(&settings);
+ BLI_task_parallel_range(0, total_scanlines, &data, processor_apply_parallel, &settings);
}
/** \} */
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index d331cfd533a..a530acb15b0 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -24,6 +24,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_fileops.h"
#include "BLI_ghash.h"
@@ -40,17 +41,16 @@
#include "IMB_indexer.h"
#include "imbuf.h"
-#include "BKE_global.h"
-
#ifdef WITH_AVI
# include "AVI_avi.h"
#endif
#ifdef WITH_FFMPEG
# include "ffmpeg_compat.h"
+# include <libavutil/imgutils.h>
#endif
-static const char magic[] = "BlenMIdx";
+static const char binary_header_str[] = "BlenMIdx";
static const char temp_ext[] = "_part";
static const int proxy_sizes[] = {IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75, IMB_PROXY_100};
@@ -65,7 +65,7 @@ static int tc_types[] = {
};
#endif
-#define INDEX_FILE_VERSION 1
+#define INDEX_FILE_VERSION 2
/* ----------------------------------------------------------------------
* - time code index functions
@@ -96,16 +96,25 @@ anim_index_builder *IMB_index_builder_create(const char *name)
return NULL;
}
- fprintf(rv->fp, "%s%c%.3d", magic, (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v', INDEX_FILE_VERSION);
+ fprintf(rv->fp,
+ "%s%c%.3d",
+ binary_header_str,
+ (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v',
+ INDEX_FILE_VERSION);
return rv;
}
-void IMB_index_builder_add_entry(
- anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts)
+void IMB_index_builder_add_entry(anim_index_builder *fp,
+ int frameno,
+ uint64_t seek_pos,
+ uint64_t seek_pos_pts,
+ uint64_t seek_pos_dts,
+ uint64_t pts)
{
fwrite(&frameno, sizeof(int), 1, fp->fp);
fwrite(&seek_pos, sizeof(uint64_t), 1, fp->fp);
+ fwrite(&seek_pos_pts, sizeof(uint64_t), 1, fp->fp);
fwrite(&seek_pos_dts, sizeof(uint64_t), 1, fp->fp);
fwrite(&pts, sizeof(uint64_t), 1, fp->fp);
}
@@ -115,6 +124,7 @@ void IMB_index_builder_proc_frame(anim_index_builder *fp,
int data_size,
int frameno,
uint64_t seek_pos,
+ uint64_t seek_pos_pts,
uint64_t seek_pos_dts,
uint64_t pts)
{
@@ -122,13 +132,14 @@ void IMB_index_builder_proc_frame(anim_index_builder *fp,
anim_index_entry e;
e.frameno = frameno;
e.seek_pos = seek_pos;
+ e.seek_pos_pts = seek_pos_pts;
e.seek_pos_dts = seek_pos_dts;
e.pts = pts;
fp->proc_frame(fp, buffer, data_size, &e);
}
else {
- IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_dts, pts);
+ IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_pts, seek_pos_dts, pts);
}
}
@@ -159,22 +170,26 @@ struct anim_index *IMB_indexer_open(const char *name)
int i;
if (!fp) {
+ fprintf(stderr, "Couldn't open indexer file: %s\n", name);
return NULL;
}
if (fread(header, 12, 1, fp) != 1) {
+ fprintf(stderr, "Couldn't read indexer file: %s\n", name);
fclose(fp);
return NULL;
}
header[12] = 0;
- if (memcmp(header, magic, 8) != 0) {
+ if (memcmp(header, binary_header_str, 8) != 0) {
+ fprintf(stderr, "Error reading %s: Binary file type string missmatch\n", name);
fclose(fp);
return NULL;
}
if (atoi(header + 9) != INDEX_FILE_VERSION) {
+ fprintf(stderr, "Error reading %s: File version missmatch\n", name);
fclose(fp);
return NULL;
}
@@ -187,6 +202,7 @@ struct anim_index *IMB_indexer_open(const char *name)
idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */
sizeof(uint64_t) + /* seek_pos */
+ sizeof(uint64_t) + /* seek_pos_pts */
sizeof(uint64_t) + /* seek_pos_dts */
sizeof(uint64_t) /* pts */
);
@@ -200,12 +216,13 @@ struct anim_index *IMB_indexer_open(const char *name)
for (i = 0; i < idx->num_entries; i++) {
items_read += fread(&idx->entries[i].frameno, sizeof(int), 1, fp);
items_read += fread(&idx->entries[i].seek_pos, sizeof(uint64_t), 1, fp);
+ items_read += fread(&idx->entries[i].seek_pos_pts, sizeof(uint64_t), 1, fp);
items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(uint64_t), 1, fp);
items_read += fread(&idx->entries[i].pts, sizeof(uint64_t), 1, fp);
}
- if (UNLIKELY(items_read != idx->num_entries * 4)) {
- perror("error reading animation index file");
+ if (UNLIKELY(items_read != idx->num_entries * 5)) {
+ fprintf(stderr, "Error: Element data size missmatch in: %s\n", name);
MEM_freeN(idx->entries);
MEM_freeN(idx);
fclose(fp);
@@ -216,6 +233,7 @@ struct anim_index *IMB_indexer_open(const char *name)
for (i = 0; i < idx->num_entries; i++) {
BLI_endian_switch_int32(&idx->entries[i].frameno);
BLI_endian_switch_uint64(&idx->entries[i].seek_pos);
+ BLI_endian_switch_uint64(&idx->entries[i].seek_pos_pts);
BLI_endian_switch_uint64(&idx->entries[i].seek_pos_dts);
BLI_endian_switch_uint64(&idx->entries[i].pts);
}
@@ -237,6 +255,17 @@ uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index)
return idx->entries[frame_index].seek_pos;
}
+uint64_t IMB_indexer_get_seek_pos_pts(struct anim_index *idx, int frame_index)
+{
+ if (frame_index < 0) {
+ frame_index = 0;
+ }
+ if (frame_index >= idx->num_entries) {
+ frame_index = idx->num_entries - 1;
+ }
+ return idx->entries[frame_index].seek_pos_pts;
+}
+
uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index)
{
if (frame_index < 0) {
@@ -318,8 +347,7 @@ int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
{
switch (pr_size) {
case IMB_PROXY_NONE:
- /* if we got here, something is broken anyways, so sane defaults... */
- return 0;
+ return -1;
case IMB_PROXY_25:
return 0;
case IMB_PROXY_50:
@@ -329,16 +357,16 @@ int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
case IMB_PROXY_100:
return 3;
default:
- return 0;
+ BLI_assert(!"Unhandled proxy size enum!");
+ return -1;
}
}
int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
{
switch (tc) {
- case IMB_TC_NONE: /* if we got here, something is broken anyways,
- * so sane defaults... */
- return 0;
+ case IMB_TC_NONE:
+ return -1;
case IMB_TC_RECORD_RUN:
return 0;
case IMB_TC_FREE_RUN:
@@ -348,7 +376,8 @@ int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
case IMB_TC_RECORD_RUN_NO_GAPS:
return 3;
default:
- return 0;
+ BLI_assert(!"Unhandled timecode type enum!");
+ return -1;
}
}
@@ -384,6 +413,8 @@ static bool get_proxy_filename(struct anim *anim,
char index_dir[FILE_MAXDIR];
int i = IMB_proxy_size_to_array_index(preview_size);
+ BLI_assert(i >= 0);
+
char proxy_name[256];
char stream_suffix[20];
const char *name = (temp) ? "proxy_%d%s_part.avi" : "proxy_%d%s.avi";
@@ -415,6 +446,9 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname
{
char index_dir[FILE_MAXDIR];
int i = IMB_timecode_to_array_index(tc);
+
+ BLI_assert(i >= 0);
+
const char *index_names[] = {
"record_run%s%s.blen_tc",
"free_run%s%s.blen_tc",
@@ -465,13 +499,6 @@ struct proxy_output_ctx {
struct anim *anim;
};
-// work around stupid swscaler 16 bytes alignment bug...
-
-static int round_up(int x, int mod)
-{
- return x + ((mod - (x % mod)) % mod);
-}
-
static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality)
{
@@ -488,23 +515,16 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->of = avformat_alloc_context();
rv->of->oformat = av_guess_format("avi", NULL, NULL);
- BLI_strncpy(rv->of->filename, fname, sizeof(rv->of->filename));
+ rv->of->url = av_strdup(fname);
- fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename);
+ fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url);
rv->st = avformat_new_stream(rv->of, NULL);
rv->st->id = 0;
- rv->c = rv->st->codec;
+ rv->c = avcodec_alloc_context3(NULL);
rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
rv->c->codec_id = AV_CODEC_ID_H264;
- rv->c->width = width;
- rv->c->height = height;
- rv->c->gop_size = 2;
- rv->c->max_b_frames = 0;
- /* Correct wrong default ffmpeg param which crash x264. */
- rv->c->qmin = 10;
- rv->c->qmax = 51;
rv->of->oformat->video_codec = rv->c->codec_id;
rv->codec = avcodec_find_encoder(rv->c->codec_id);
@@ -513,10 +533,19 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
fprintf(stderr,
"No ffmpeg encoder available? "
"Proxy not built!\n");
- av_free(rv->of);
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
return NULL;
}
+ avcodec_get_context_defaults3(rv->c, rv->codec);
+
+ rv->c->width = width;
+ rv->c->height = height;
+ rv->c->gop_size = 10;
+ rv->c->max_b_frames = 0;
+
if (rv->codec->pix_fmts) {
rv->c->pix_fmt = rv->codec->pix_fmts[0];
}
@@ -524,14 +553,14 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P;
}
- rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio;
+ rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->sample_aspect_ratio;
rv->c->time_base.den = 25;
rv->c->time_base.num = 1;
rv->st->time_base = rv->c->time_base;
- /* This range matches eFFMpegCrf. Crf_range_min corresponds to lowest quality, crf_range_max to
- * highest quality. */
+ /* This range matches #eFFMpegCrf. `crf_range_min` corresponds to lowest quality,
+ * `crf_range_max` to highest quality. */
const int crf_range_min = 32;
const int crf_range_max = 17;
int crf = round_fl_to_int((quality / 100.0f) * (crf_range_max - crf_range_min) + crf_range_min);
@@ -539,40 +568,79 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
AVDictionary *codec_opts = NULL;
/* High quality preset value. */
av_dict_set_int(&codec_opts, "crf", crf, 0);
- /* Prefer smaller file-size. */
- av_dict_set(&codec_opts, "preset", "slow", 0);
- /* Thread count. */
- av_dict_set_int(&codec_opts, "threads", BLI_system_thread_count(), 0);
+ /* Prefer smaller file-size. Presets from `veryslow` to `veryfast` produce output with very
+ * similar file-size, but there is big difference in performance.
+ * In some cases `veryfast` preset will produce smallest file-size. */
+ av_dict_set(&codec_opts, "preset", "veryfast", 0);
+ av_dict_set(&codec_opts, "tune", "fastdecode", 0);
+
+ if (rv->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ rv->c->thread_count = 0;
+ }
+ else {
+ rv->c->thread_count = BLI_system_thread_count();
+ }
+
+ if (rv->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ rv->c->thread_type = FF_THREAD_FRAME;
+ }
+ else if (rv->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ rv->c->thread_type = FF_THREAD_SLICE;
+ }
if (rv->of->flags & AVFMT_GLOBALHEADER) {
- rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ rv->c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
- if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) {
+ avcodec_parameters_from_context(rv->st->codecpar, rv->c);
+
+ int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE);
+
+ if (ret < 0) {
fprintf(stderr,
- "Couldn't open outputfile! "
- "Proxy not built!\n");
- av_free(rv->of);
- return 0;
+ "Couldn't open IO: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
}
- avcodec_open2(rv->c, rv->codec, &codec_opts);
+ ret = avcodec_open2(rv->c, rv->codec, &codec_opts);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Couldn't open codec: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
+ }
- rv->orig_height = av_get_cropped_height_from_codec(st->codec);
+ rv->orig_height = st->codecpar->height;
- if (st->codec->width != width || st->codec->height != height ||
- st->codec->pix_fmt != rv->c->pix_fmt) {
+ if (st->codecpar->width != width || st->codecpar->height != height ||
+ st->codecpar->format != rv->c->pix_fmt) {
rv->frame = av_frame_alloc();
- avpicture_fill((AVPicture *)rv->frame,
- MEM_mallocN(avpicture_get_size(rv->c->pix_fmt, round_up(width, 16), height),
- "alloc proxy output frame"),
- rv->c->pix_fmt,
- round_up(width, 16),
- height);
-
- rv->sws_ctx = sws_getContext(st->codec->width,
+
+ av_image_fill_arrays(rv->frame->data,
+ rv->frame->linesize,
+ MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, width, height, 1),
+ "alloc proxy output frame"),
+ rv->c->pix_fmt,
+ width,
+ height,
+ 1);
+
+ rv->frame->format = rv->c->pix_fmt;
+ rv->frame->width = width;
+ rv->frame->height = height;
+
+ rv->sws_ctx = sws_getContext(st->codecpar->width,
rv->orig_height,
- st->codec->pix_fmt,
+ st->codecpar->format,
width,
height,
rv->c->pix_fmt,
@@ -582,26 +650,30 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
NULL);
}
- if (avformat_write_header(rv->of, NULL) < 0) {
+ ret = avformat_write_header(rv->of, NULL);
+ if (ret < 0) {
fprintf(stderr,
- "Couldn't set output parameters? "
- "Proxy not built!\n");
- av_free(rv->of);
- return 0;
+ "Couldn't write header: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+
+ if (rv->frame) {
+ av_frame_free(&rv->frame);
+ }
+
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
}
return rv;
}
-static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame)
+static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame)
{
- AVPacket packet = {0};
- int ret, got_output;
-
- av_init_packet(&packet);
-
if (!ctx) {
- return 0;
+ return;
}
if (ctx->sws_ctx && frame &&
@@ -621,35 +693,49 @@ static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fra
frame->pts = ctx->cfra++;
}
- ret = avcodec_encode_video2(ctx->c, &packet, frame, &got_output);
+ int ret = avcodec_send_frame(ctx->c, frame);
if (ret < 0) {
- fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", ctx->cfra - 1, ctx->of->filename);
- return 0;
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret));
+ return;
}
+ AVPacket *packet = av_packet_alloc();
+
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(ctx->c, packet);
- if (got_output) {
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, ctx->c->time_base, ctx->st->time_base);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets to flush. */
+ break;
}
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, ctx->c->time_base, ctx->st->time_base);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Error encoding proxy frame %d for '%s': %s\n",
+ ctx->cfra - 1,
+ ctx->of->url,
+ av_err2str(ret));
+ break;
}
- packet.stream_index = ctx->st->index;
+ packet->stream_index = ctx->st->index;
+ av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(ctx->of, ctx->st, packet);
+# endif
- if (av_interleaved_write_frame(ctx->of, &packet) != 0) {
+ int write_ret = av_interleaved_write_frame(ctx->of, packet);
+ if (write_ret != 0) {
fprintf(stderr,
"Error writing proxy frame %d "
- "into '%s'\n",
+ "into '%s': %s\n",
ctx->cfra - 1,
- ctx->of->filename);
- return 0;
+ ctx->of->url,
+ av_err2str(write_ret));
+ break;
}
-
- return 1;
}
- return 0;
+ av_packet_free(&packet);
}
static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback)
@@ -662,15 +748,15 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback)
}
if (!rollback) {
- while (add_to_proxy_output_ffmpeg(ctx, NULL)) {
- }
+ /* Flush the remaining packets. */
+ add_to_proxy_output_ffmpeg(ctx, NULL);
}
avcodec_flush_buffers(ctx->c);
av_write_trailer(ctx->of);
- avcodec_close(ctx->c);
+ avcodec_free_context(&ctx->c);
if (ctx->of->oformat) {
if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
@@ -719,9 +805,10 @@ typedef struct FFmpegIndexBuilderContext {
IMB_Proxy_Size proxy_sizes_in_use;
uint64_t seek_pos;
- uint64_t last_seek_pos;
- uint64_t seek_pos_dts;
uint64_t seek_pos_pts;
+ uint64_t seek_pos_dts;
+ uint64_t last_seek_pos;
+ uint64_t last_seek_pos_pts;
uint64_t last_seek_pos_dts;
uint64_t start_pts;
double frame_rate;
@@ -765,7 +852,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
/* Find the video stream */
context->videoStream = -1;
for (i = 0; i < context->iFormatCtx->nb_streams; i++) {
- if (context->iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (context->iFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (streamcount > 0) {
streamcount--;
continue;
@@ -782,9 +869,8 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
}
context->iStream = context->iFormatCtx->streams[context->videoStream];
- context->iCodecCtx = context->iStream->codec;
- context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id);
+ context->iCodec = avcodec_find_decoder(context->iStream->codecpar->codec_id);
if (context->iCodec == NULL) {
avformat_close_input(&context->iFormatCtx);
@@ -792,27 +878,39 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
return NULL;
}
- context->iCodecCtx->workaround_bugs = 1;
+ context->iCodecCtx = avcodec_alloc_context3(NULL);
+ avcodec_parameters_to_context(context->iCodecCtx, context->iStream->codecpar);
+ context->iCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
- AVDictionary *codec_opts = NULL;
- /* Thread count. */
- av_dict_set_int(&codec_opts, "threads", BLI_system_thread_count(), 0);
+ if (context->iCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ context->iCodecCtx->thread_count = 0;
+ }
+ else {
+ context->iCodecCtx->thread_count = BLI_system_thread_count();
+ }
- if (avcodec_open2(context->iCodecCtx, context->iCodec, &codec_opts) < 0) {
+ if (context->iCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ context->iCodecCtx->thread_type = FF_THREAD_FRAME;
+ }
+ else if (context->iCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ context->iCodecCtx->thread_type = FF_THREAD_SLICE;
+ }
+
+ if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) {
avformat_close_input(&context->iFormatCtx);
+ avcodec_free_context(&context->iCodecCtx);
MEM_freeN(context);
return NULL;
}
for (i = 0; i < num_proxy_sizes; i++) {
if (proxy_sizes_in_use & proxy_sizes[i]) {
- context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(
- anim,
- context->iStream,
- proxy_sizes[i],
- context->iCodecCtx->width * proxy_fac[i],
- av_get_cropped_height_from_codec(context->iCodecCtx) * proxy_fac[i],
- quality);
+ context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(anim,
+ context->iStream,
+ proxy_sizes[i],
+ context->iCodecCtx->width * proxy_fac[i],
+ context->iCodecCtx->height * proxy_fac[i],
+ quality);
if (!context->proxy_ctx[i]) {
proxy_sizes_in_use &= ~proxy_sizes[i];
}
@@ -851,7 +949,7 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int
}
}
- avcodec_close(context->iCodecCtx);
+ avcodec_free_context(&context->iCodecCtx);
avformat_close_input(&context->iFormatCtx);
MEM_freeN(context);
@@ -863,8 +961,9 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c
{
int i;
uint64_t s_pos = context->seek_pos;
+ uint64_t s_pts = context->seek_pos_pts;
uint64_t s_dts = context->seek_pos_dts;
- uint64_t pts = av_get_pts_from_frame(context->iFormatCtx, in_frame);
+ uint64_t pts = av_get_pts_from_frame(in_frame);
for (i = 0; i < context->num_proxy_sizes; i++) {
add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame);
@@ -878,15 +977,15 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c
context->frameno = floor(
(pts - context->start_pts) * context->pts_time_base * context->frame_rate + 0.5);
- /* decoding starts *always* on I-Frames,
- * so: P-Frames won't work, even if all the
- * information is in place, when we seek
- * to the I-Frame presented *after* the P-Frame,
- * but located before the P-Frame within
- * the stream */
+ int64_t seek_pos_pts = timestamp_from_pts_or_dts(context->seek_pos_pts, context->seek_pos_dts);
- if (pts < context->seek_pos_pts) {
+ if (pts < seek_pos_pts) {
+ /* Decoding starts *always* on I-Frames. In this case our position is
+ * before our seek I-Frame. So we need to pick the previous available
+ * I-Frame to be able to decode this one properly.
+ */
s_pos = context->last_seek_pos;
+ s_pts = context->last_seek_pos_pts;
s_dts = context->last_seek_pos_dts;
}
@@ -903,6 +1002,7 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c
curr_packet->size,
tc_frameno,
s_pos,
+ s_pts,
s_dts,
pts);
}
@@ -916,23 +1016,18 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
short *do_update,
float *progress)
{
- AVFrame *in_frame = 0;
- AVPacket next_packet;
+ AVFrame *in_frame = av_frame_alloc();
+ AVPacket *next_packet = av_packet_alloc();
uint64_t stream_size;
- memset(&next_packet, 0, sizeof(AVPacket));
-
- in_frame = av_frame_alloc();
-
stream_size = avio_size(context->iFormatCtx->pb);
context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL));
context->pts_time_base = av_q2d(context->iStream->time_base);
- while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) {
- int frame_finished = 0;
+ while (av_read_frame(context->iFormatCtx, next_packet) >= 0) {
float next_progress =
- (float)((int)floor(((double)next_packet.pos) * 100 / ((double)stream_size) + 0.5)) / 100;
+ (float)((int)floor(((double)next_packet->pos) * 100 / ((double)stream_size) + 0.5)) / 100;
if (*progress != next_progress) {
*progress = next_progress;
@@ -940,50 +1035,62 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
}
if (*stop) {
- av_free_packet(&next_packet);
break;
}
- if (next_packet.stream_index == context->videoStream) {
- if (next_packet.flags & AV_PKT_FLAG_KEY) {
+ if (next_packet->stream_index == context->videoStream) {
+ if (next_packet->flags & AV_PKT_FLAG_KEY) {
context->last_seek_pos = context->seek_pos;
+ context->last_seek_pos_pts = context->seek_pos_pts;
context->last_seek_pos_dts = context->seek_pos_dts;
- context->seek_pos = next_packet.pos;
- context->seek_pos_dts = next_packet.dts;
- context->seek_pos_pts = next_packet.pts;
+
+ context->seek_pos = next_packet->pos;
+ context->seek_pos_pts = next_packet->pts;
+ context->seek_pos_dts = next_packet->dts;
}
- avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
- }
+ int ret = avcodec_send_packet(context->iCodecCtx, next_packet);
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
- if (frame_finished) {
- index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more frames to flush. */
+ break;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret));
+ break;
+ }
+ index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame);
+ }
}
- av_free_packet(&next_packet);
+ av_packet_unref(next_packet);
}
/* process pictures still stuck in decoder engine after EOF
- * according to ffmpeg docs using 0-size packets.
+ * according to ffmpeg docs using NULL packets.
*
* At least, if we haven't already stopped... */
- /* this creates the 0-size packet and prevents a memory leak. */
- av_free_packet(&next_packet);
-
if (!*stop) {
- int frame_finished;
+ int ret = avcodec_send_packet(context->iCodecCtx, NULL);
- do {
- frame_finished = 0;
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
- avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
-
- if (frame_finished) {
- index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more frames to flush. */
+ break;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Error flushing proxy frame: %s\n", av_err2str(ret));
+ break;
}
- } while (frame_finished);
+ index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame);
+ }
}
+ av_packet_free(&next_packet);
av_free(in_frame);
return 1;
@@ -1321,6 +1428,10 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size)
char fname[FILE_MAX];
int i = IMB_proxy_size_to_array_index(preview_size);
+ if (i < 0) {
+ return NULL;
+ }
+
if (anim->proxy_anim[i]) {
return anim->proxy_anim[i];
}
@@ -1344,6 +1455,10 @@ struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc)
char fname[FILE_MAX];
int i = IMB_timecode_to_array_index(tc);
+ if (i < 0) {
+ return NULL;
+ }
+
if (anim->curr_idx[i]) {
return anim->curr_idx[i];
}
diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c
index 112b95bf1a1..547af472d73 100644
--- a/source/blender/imbuf/intern/iris.c
+++ b/source/blender/imbuf/intern/iris.c
@@ -270,11 +270,13 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
ImBuf *ibuf = NULL;
uchar dirty_flag = 0;
- if (size < HEADER_SIZE) {
+ if (!imb_is_a_iris(mem, size)) {
return NULL;
}
- if (!imb_is_a_iris(mem, size)) {
+ /* Could pe part of the magic check above,
+ * by convention this check only requests the size needed to read it's magic though. */
+ if (size < HEADER_SIZE) {
return NULL;
}
diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c
index 440375f60dc..48b5b0c34db 100644
--- a/source/blender/imbuf/intern/jpeg.c
+++ b/source/blender/imbuf/intern/jpeg.c
@@ -516,7 +516,8 @@ static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf)
* The first "Blender" is a simple identify to help
* in the read process.
*/
- text_len = BLI_snprintf(text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop));
+ text_len = BLI_snprintf_rlen(
+ text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop));
jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *)text, text_len + 1);
/* TODO(sergey): Ideally we will try to re-use allocation as
diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c
index d8abd3411cb..c59997b34f5 100644
--- a/source/blender/imbuf/intern/metadata.c
+++ b/source/blender/imbuf/intern/metadata.c
@@ -38,8 +38,6 @@
#include "IMB_metadata.h"
-#define METADATA_MAX_VALUE_LENGTH 1024
-
void IMB_metadata_ensure(struct IDProperty **metadata)
{
if (*metadata != NULL) {
@@ -99,11 +97,11 @@ void IMB_metadata_set_field(struct IDProperty *metadata, const char *key, const
}
if (prop == NULL) {
- prop = IDP_NewString(value, key, METADATA_MAX_VALUE_LENGTH);
+ prop = IDP_NewString(value, key, 0);
IDP_AddToGroup(metadata, prop);
}
- IDP_AssignString(prop, value, METADATA_MAX_VALUE_LENGTH);
+ IDP_AssignString(prop, value, 0);
}
void IMB_metadata_foreach(struct ImBuf *ibuf, IMBMetadataForeachCb callback, void *userdata)
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index 2a9cb9af891..382d86f2645 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -120,10 +120,6 @@ class IMemStream : public Imf::IStream {
_exrbuf = exrbuf;
}
- ~IMemStream() override
- {
- }
-
bool read(char c[], int n) override
{
if (n + _exrpos <= _exrsize) {
diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c
index 285b18595f7..94b2a62aa26 100644
--- a/source/blender/imbuf/intern/radiance_hdr.c
+++ b/source/blender/imbuf/intern/radiance_hdr.c
@@ -229,87 +229,89 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem,
const unsigned char *ptr, *mem_eof = mem + size;
char oriY[80], oriX[80];
- if (imb_is_a_hdr(mem, size)) {
- colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
-
- /* find empty line, next line is resolution info */
- size_t x;
- for (x = 1; x < size; x++) {
- if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
- found = 1;
- break;
- }
+ if (!imb_is_a_hdr(mem, size)) {
+ return NULL;
+ }
+
+ colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
+
+ /* find empty line, next line is resolution info */
+ size_t x;
+ for (x = 1; x < size; x++) {
+ if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
+ found = 1;
+ break;
}
- if (found && (x < (size + 2))) {
- if (sscanf((char *)&mem[x + 1],
- "%79s %d %79s %d",
- (char *)&oriY,
- &height,
- (char *)&oriX,
- &width) != 4) {
- return NULL;
- }
+ }
- /* find end of this line, data right behind it */
- ptr = (unsigned char *)strchr((char *)&mem[x + 1], '\n');
- ptr++;
+ if ((found && (x < (size + 2))) == 0) {
+ /* Data not found! */
+ return NULL;
+ }
- if (flags & IB_test) {
- ibuf = IMB_allocImBuf(width, height, 32, 0);
- }
- else {
- ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
- }
+ if (sscanf((const char *)&mem[x + 1],
+ "%79s %d %79s %d",
+ (char *)&oriY,
+ &height,
+ (char *)&oriX,
+ &width) != 4) {
+ return NULL;
+ }
- if (UNLIKELY(ibuf == NULL)) {
- return NULL;
- }
- ibuf->ftype = IMB_FTYPE_RADHDR;
+ /* find end of this line, data right behind it */
+ ptr = (const unsigned char *)strchr((const char *)&mem[x + 1], '\n');
+ ptr++;
- if (flags & IB_alphamode_detect) {
- ibuf->flags |= IB_alphamode_premul;
- }
+ if (flags & IB_test) {
+ ibuf = IMB_allocImBuf(width, height, 32, 0);
+ }
+ else {
+ ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
+ }
- if (flags & IB_test) {
- return ibuf;
- }
+ if (UNLIKELY(ibuf == NULL)) {
+ return NULL;
+ }
- /* read in and decode the actual data */
- sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);
- rect_float = ibuf->rect_float;
+ ibuf->ftype = IMB_FTYPE_RADHDR;
- for (size_t y = 0; y < height; y++) {
- ptr = freadcolrs(sline, ptr, width, mem_eof);
- if (ptr == NULL) {
- printf(
- "WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
- break;
- }
- for (x = 0; x < width; x++) {
- /* convert to ldr */
- RGBE2FLOAT(sline[x], fcol);
- *rect_float++ = fcol[RED];
- *rect_float++ = fcol[GRN];
- *rect_float++ = fcol[BLU];
- *rect_float++ = 1.0f;
- }
- }
- MEM_freeN(sline);
- if (oriY[0] == '-') {
- IMB_flipy(ibuf);
- }
+ if (flags & IB_alphamode_detect) {
+ ibuf->flags |= IB_alphamode_premul;
+ }
- if (flags & IB_rect) {
- IMB_rect_from_float(ibuf);
- }
+ if (flags & IB_test) {
+ return ibuf;
+ }
- return ibuf;
+ /* read in and decode the actual data */
+ sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);
+ rect_float = ibuf->rect_float;
+
+ for (size_t y = 0; y < height; y++) {
+ ptr = freadcolrs(sline, ptr, width, mem_eof);
+ if (ptr == NULL) {
+ printf("WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
+ break;
}
- // else printf("Data not found!\n");
+ for (x = 0; x < width; x++) {
+ /* convert to ldr */
+ RGBE2FLOAT(sline[x], fcol);
+ *rect_float++ = fcol[RED];
+ *rect_float++ = fcol[GRN];
+ *rect_float++ = fcol[BLU];
+ *rect_float++ = 1.0f;
+ }
+ }
+ MEM_freeN(sline);
+ if (oriY[0] == '-') {
+ IMB_flipy(ibuf);
+ }
+
+ if (flags & IB_rect) {
+ IMB_rect_from_float(ibuf);
}
- // else printf("Not a valid radiance HDR file!\n");
- return NULL;
+ return ibuf;
}
/* ImBuf write */
diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c
index 6ae93def50f..4b5d68b9c13 100644
--- a/source/blender/imbuf/intern/rectop.c
+++ b/source/blender/imbuf/intern/rectop.c
@@ -988,8 +988,9 @@ typedef struct RectBlendThreadData {
bool accumulate;
} RectBlendThreadData;
-static void rectblend_thread_do(void *data_v, int start_scanline, int num_scanlines)
+static void rectblend_thread_do(void *data_v, int scanline)
{
+ const int num_scanlines = 1;
RectBlendThreadData *data = (RectBlendThreadData *)data_v;
IMB_rectblend(data->dbuf,
data->obuf,
@@ -999,11 +1000,11 @@ static void rectblend_thread_do(void *data_v, int start_scanline, int num_scanli
data->texmask,
data->mask_max,
data->destx,
- data->desty + start_scanline,
+ data->desty + scanline,
data->origx,
- data->origy + start_scanline,
+ data->origy + scanline,
data->srcx,
- data->srcy + start_scanline,
+ data->srcy + scanline,
data->width,
num_scanlines,
data->mode,
diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c
index 2fb14e40d9d..d9e1db27ef0 100644
--- a/source/blender/imbuf/intern/tiff.c
+++ b/source/blender/imbuf/intern/tiff.c
@@ -38,6 +38,7 @@
#include "imbuf.h"
+#include "BLI_endian_defines.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -374,7 +375,7 @@ static void scanline_separate_32bit(float *rectf, const float *fbuf, int scanlin
static void imb_read_tiff_resolution(ImBuf *ibuf, TIFF *image)
{
- uint16 unit;
+ uint16_t unit;
float xres;
float yres;
@@ -569,18 +570,14 @@ ImBuf *imb_loadtiff(const unsigned char *mem,
TIFF *image = NULL;
ImBuf *ibuf = NULL, *hbuf;
ImbTIFFMemFile memFile;
- uint32 width, height;
+ uint32_t width, height;
char *format = NULL;
int level;
short spp;
int ib_depth;
int found;
- /* check whether or not we have a TIFF file */
- if (size < IMB_TIFF_NCB) {
- fprintf(stderr, "imb_loadtiff: size < IMB_TIFF_NCB\n");
- return NULL;
- }
+ /* Check whether or not we have a TIFF file. */
if (imb_is_a_tiff(mem, size) == 0) {
return NULL;
}
@@ -694,7 +691,7 @@ void imb_loadtiletiff(
ImBuf *ibuf, const unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect)
{
TIFF *image = NULL;
- uint32 width, height;
+ uint32_t width, height;
ImbTIFFMemFile memFile;
image = imb_tiff_client_open(&memFile, mem, size);
@@ -765,7 +762,7 @@ void imb_loadtiletiff(
bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
{
TIFF *image = NULL;
- uint16 samplesperpixel, bitspersample;
+ uint16_t samplesperpixel, bitspersample;
size_t npixels;
unsigned char *pixels = NULL;
unsigned char *from = NULL, *to = NULL;
@@ -778,7 +775,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
/* check for a valid number of bytes per pixel. Like the PNG writer,
* the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding
* to gray, RGB, RGBA respectively. */
- samplesperpixel = (uint16)((ibuf->planes + 7) >> 3);
+ samplesperpixel = (uint16_t)((ibuf->planes + 7) >> 3);
if ((samplesperpixel > 4) || (samplesperpixel == 2)) {
fprintf(stderr,
"imb_savetiff: unsupported number of bytes per "
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index 64dad5de902..1bb047f1317 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -182,7 +182,7 @@ bool IMB_ispic_type_matches(const char *filepath, int filetype)
const ImFileType *type = IMB_file_type_from_ftype(filetype);
if (type != NULL) {
- /* Requesting to load a type that can't check it's own header doesn't make sense.
+ /* Requesting to load a type that can't check its own header doesn't make sense.
* Keep the check for developers. */
BLI_assert(type->is_a != NULL);
if (type->is_a != NULL) {
@@ -245,7 +245,6 @@ static void ffmpeg_log_callback(void *ptr, int level, const char *format, va_lis
void IMB_ffmpeg_init(void)
{
- av_register_all();
avdevice_register_all();
ffmpeg_last_error[0] = '\0';
@@ -269,7 +268,6 @@ static int isffmpeg(const char *filepath)
unsigned int i;
int videoStream;
AVCodec *pCodec;
- AVCodecContext *pCodecCtx;
if (BLI_path_extension_check_n(filepath,
".swf",
@@ -310,8 +308,8 @@ static int isffmpeg(const char *filepath)
/* Find the first video stream */
videoStream = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
- if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codec &&
- (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)) {
+ if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codecpar &&
+ (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) {
videoStream = i;
break;
}
@@ -322,21 +320,15 @@ static int isffmpeg(const char *filepath)
return 0;
}
- pCodecCtx = pFormatCtx->streams[videoStream]->codec;
+ AVCodecParameters *codec_par = pFormatCtx->streams[videoStream]->codecpar;
/* Find the decoder for the video stream */
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+ pCodec = avcodec_find_decoder(codec_par->codec_id);
if (pCodec == NULL) {
avformat_close_input(&pFormatCtx);
return 0;
}
- if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
- avformat_close_input(&pFormatCtx);
- return 0;
- }
-
- avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 1;
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index 9785f6d68ab..5664a43233a 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -49,6 +49,7 @@ struct AlembicExportParams {
bool uvs;
bool normals;
bool vcolors;
+ bool orcos;
bool apply_subdiv;
bool curves_as_mesh;
bool flatten_hierarchy;
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc
index 382afdc294d..f5593e7ee30 100644
--- a/source/blender/io/alembic/exporter/abc_custom_props.cc
+++ b/source/blender/io/alembic/exporter/abc_custom_props.cc
@@ -51,10 +51,6 @@ CustomPropertiesExporter::CustomPropertiesExporter(ABCAbstractWriter *owner) : o
{
}
-CustomPropertiesExporter::~CustomPropertiesExporter()
-{
-}
-
void CustomPropertiesExporter::write_all(const IDProperty *group)
{
if (group == nullptr) {
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.h b/source/blender/io/alembic/exporter/abc_custom_props.h
index d3f9b2fc43c..fcd47382d96 100644
--- a/source/blender/io/alembic/exporter/abc_custom_props.h
+++ b/source/blender/io/alembic/exporter/abc_custom_props.h
@@ -61,7 +61,7 @@ class CustomPropertiesExporter {
public:
CustomPropertiesExporter(ABCAbstractWriter *owner);
- virtual ~CustomPropertiesExporter();
+ virtual ~CustomPropertiesExporter() = default;
void write_all(const IDProperty *group);
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
index e99048cc0ee..27b5c2fa2a4 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
@@ -45,10 +45,6 @@ ABCAbstractWriter::ABCAbstractWriter(const ABCWriterConstructorArgs &args)
{
}
-ABCAbstractWriter::~ABCAbstractWriter()
-{
-}
-
bool ABCAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
{
return true;
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h
index 4997e498199..d3500394555 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.h
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h
@@ -50,7 +50,6 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
public:
explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args);
- virtual ~ABCAbstractWriter();
virtual void write(HierarchyContext &context) override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.cc b/source/blender/io/alembic/exporter/abc_writer_instance.cc
index 7f3b044cb8b..1737e8c091e 100644
--- a/source/blender/io/alembic/exporter/abc_writer_instance.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_instance.cc
@@ -35,10 +35,6 @@ ABCInstanceWriter::ABCInstanceWriter(const ABCWriterConstructorArgs &args)
{
}
-ABCInstanceWriter::~ABCInstanceWriter()
-{
-}
-
void ABCInstanceWriter::create_alembic_objects(const HierarchyContext *context)
{
OObject original = args_.hierarchy_iterator->get_alembic_object(context->original_export_path);
diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.h b/source/blender/io/alembic/exporter/abc_writer_instance.h
index f7d6450055a..1e4285d5e02 100644
--- a/source/blender/io/alembic/exporter/abc_writer_instance.h
+++ b/source/blender/io/alembic/exporter/abc_writer_instance.h
@@ -31,7 +31,6 @@ namespace blender::io::alembic {
class ABCInstanceWriter : public ABCAbstractWriter {
public:
explicit ABCInstanceWriter(const ABCWriterConstructorArgs &args);
- virtual ~ABCInstanceWriter();
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.cc b/source/blender/io/alembic/exporter/abc_writer_mball.cc
index a797310f864..ad78f8ce802 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mball.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mball.cc
@@ -69,7 +69,7 @@ Mesh *ABCMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
return mesh_eval;
}
r_needsfree = true;
- return BKE_mesh_new_from_object(args_.depsgraph, object_eval, false);
+ return BKE_mesh_new_from_object(args_.depsgraph, object_eval, false, false);
}
void ABCMetaballWriter::free_export_mesh(Mesh *mesh)
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index fbc662113cc..fd7db005dd2 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -113,10 +113,6 @@ void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *contex
liquid_sim_modifier_ = get_liquid_sim_modifier(scene_eval, context->object);
}
-ABCGenericMeshWriter::~ABCGenericMeshWriter()
-{
-}
-
Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const
{
if (is_subd_) {
@@ -204,6 +200,7 @@ void ABCGenericMeshWriter::do_write(HierarchyContext &context)
}
m_custom_data_config.pack_uvs = args_.export_params->packuv;
+ m_custom_data_config.mesh = mesh;
m_custom_data_config.mpoly = mesh->mpoly;
m_custom_data_config.mloop = mesh->mloop;
m_custom_data_config.totpoly = mesh->totpoly;
@@ -254,7 +251,7 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
UVSample uvs_and_indices;
- if (!frame_has_been_written_ && args_.export_params->uvs) {
+ if (args_.export_params->uvs) {
const char *name = get_uv_sample(uvs_and_indices, m_custom_data_config, &mesh->ldata);
if (!uvs_and_indices.indices.empty() && !uvs_and_indices.uvs.empty()) {
@@ -283,6 +280,10 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
mesh_sample.setNormals(normals_sample);
}
+ if (args_.export_params->orcos) {
+ write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config);
+ }
+
if (liquid_sim_modifier_ != nullptr) {
get_velocities(mesh, velocities);
mesh_sample.setVelocities(V3fArraySample(velocities));
@@ -316,7 +317,7 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
UVSample sample;
- if (!frame_has_been_written_ && args_.export_params->uvs) {
+ if (args_.export_params->uvs) {
const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata);
if (!sample.indices.empty() && !sample.uvs.empty()) {
@@ -333,6 +334,10 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me
abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
}
+ if (args_.export_params->orcos) {
+ write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config);
+ }
+
if (!crease_indices.empty()) {
subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h
index ed4fb4e4514..0e1792b9dab 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.h
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h
@@ -51,7 +51,6 @@ class ABCGenericMeshWriter : public ABCAbstractWriter {
public:
explicit ABCGenericMeshWriter(const ABCWriterConstructorArgs &args);
- virtual ~ABCGenericMeshWriter();
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc
index 66e05504303..ccf353595c9 100644
--- a/source/blender/io/alembic/intern/abc_customdata.cc
+++ b/source/blender/io/alembic/intern/abc_customdata.cc
@@ -22,12 +22,14 @@
*/
#include "abc_customdata.h"
+#include "abc_axis_conversion.h"
#include <Alembic/AbcGeom/All.h>
#include <algorithm>
#include <unordered_map>
#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_math_base.h"
@@ -50,8 +52,13 @@ using Alembic::Abc::V2fArraySample;
using Alembic::AbcGeom::OC4fGeomParam;
using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OV3fGeomParam;
namespace blender::io::alembic {
+/* ORCO, Generated Coordinates, and Reference Points ("Pref") are all terms for the same thing.
+ * Other applications (Maya, Houdini) write these to a property called "Pref". */
+static const std::string propNameOriginalCoordinates("Pref");
+
static void get_uvs(const CDStreamConfig &config,
std::vector<Imath::V2f> &uvs,
std::vector<uint32_t> &uvidx,
@@ -222,6 +229,32 @@ static void write_mcol(const OCompoundProperty &prop,
param.set(sample);
}
+void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config)
+{
+ const void *customdata = CustomData_get_layer(&config.mesh->vdata, CD_ORCO);
+ if (customdata == nullptr) {
+ /* Data not available, so don't even bother creating an Alembic property for it. */
+ return;
+ }
+ const float(*orcodata)[3] = static_cast<const float(*)[3]>(customdata);
+
+ /* Convert 3D vertices from float[3] z=up to V3f y=up. */
+ std::vector<Imath::V3f> coords(config.totvert);
+ float orco_yup[3];
+ for (int vertex_idx = 0; vertex_idx < config.totvert; vertex_idx++) {
+ copy_yup_from_zup(orco_yup, orcodata[vertex_idx]);
+ coords[vertex_idx].setValue(orco_yup[0], orco_yup[1], orco_yup[2]);
+ }
+
+ if (!config.abc_ocro.valid()) {
+ /* Create the Alembic property and keep a reference so future frames can reuse it. */
+ config.abc_ocro = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1);
+ }
+
+ OV3fGeomParam::Sample sample(coords, kVertexScope);
+ config.abc_ocro.set(sample);
+}
+
void write_custom_data(const OCompoundProperty &prop,
CDStreamConfig &config,
CustomData *data,
@@ -263,6 +296,7 @@ using Alembic::Abc::PropertyHeader;
using Alembic::AbcGeom::IC3fGeomParam;
using Alembic::AbcGeom::IC4fGeomParam;
using Alembic::AbcGeom::IV2fGeomParam;
+using Alembic::AbcGeom::IV3fGeomParam;
static void read_uvs(const CDStreamConfig &config,
void *data,
@@ -448,6 +482,44 @@ static void read_custom_data_uvs(const ICompoundProperty &prop,
read_uvs(config, cd_data, sample.getVals(), sample.getIndices());
}
+void read_generated_coordinates(const ICompoundProperty &prop,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss)
+{
+ if (prop.getPropertyHeader(propNameOriginalCoordinates) == nullptr) {
+ /* The ORCO property isn't there, so don't bother trying to process it. */
+ return;
+ }
+
+ IV3fGeomParam param(prop, propNameOriginalCoordinates);
+ if (!param.valid() || param.isIndexed()) {
+ /* Invalid or indexed coordinates aren't supported. */
+ return;
+ }
+ if (param.getScope() != kVertexScope) {
+ /* These are original vertex coordinates, so must be vertex-scoped. */
+ return;
+ }
+
+ IV3fGeomParam::Sample sample = param.getExpandedValue(iss);
+ Alembic::AbcGeom::V3fArraySamplePtr abc_ocro = sample.getVals();
+ const size_t totvert = abc_ocro.get()->size();
+
+ void *cd_data;
+ if (CustomData_has_layer(&config.mesh->vdata, CD_ORCO)) {
+ cd_data = CustomData_get_layer(&config.mesh->vdata, CD_ORCO);
+ }
+ else {
+ cd_data = CustomData_add_layer(&config.mesh->vdata, CD_ORCO, CD_CALLOC, nullptr, totvert);
+ }
+
+ float(*orcodata)[3] = static_cast<float(*)[3]>(cd_data);
+ for (int vertex_idx = 0; vertex_idx < totvert; ++vertex_idx) {
+ const Imath::V3f &abc_coords = (*abc_ocro)[vertex_idx];
+ copy_zup_from_yup(orcodata[vertex_idx], abc_coords.getValue());
+ }
+}
+
void read_custom_data(const std::string &iobject_full_name,
const ICompoundProperty &prop,
const CDStreamConfig &config,
diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h
index 4eb515f132c..9ee964c0545 100644
--- a/source/blender/io/alembic/intern/abc_customdata.h
+++ b/source/blender/io/alembic/intern/abc_customdata.h
@@ -72,12 +72,16 @@ struct CDStreamConfig {
const char **modifier_error_message;
- /* Alembic needs Blender to keep references to C++ objects (the destructors
- * finalize the writing to ABC). This map stores OV2fGeomParam objects for the
- * 2nd and subsequent UV maps; the primary UV map is kept alive by the Alembic
- * mesh sample itself. */
+ /* Alembic needs Blender to keep references to C++ objects (the destructors finalize the writing
+ * to ABC). The following fields are all used to keep these references. */
+
+ /* Mapping from UV map name to its ABC property, for the 2nd and subsequent UV maps; the primary
+ * UV map is kept alive by the Alembic mesh sample itself. */
std::map<std::string, Alembic::AbcGeom::OV2fGeomParam> abc_uv_maps;
+ /* OCRO coordinates, aka Generated Coordinates. */
+ Alembic::AbcGeom::OV3fGeomParam abc_ocro;
+
CDStreamConfig()
: mloop(NULL),
totloop(0),
@@ -102,6 +106,12 @@ struct CDStreamConfig {
* For now the active layer is used, maybe needs a better way to choose this. */
const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data);
+void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config);
+
+void read_generated_coordinates(const ICompoundProperty &prop,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss);
+
void write_custom_data(const OCompoundProperty &prop,
CDStreamConfig &config,
CustomData *data,
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index 8133f615080..11b6c1c18ca 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -439,6 +439,7 @@ static void read_mesh_sample(const std::string &iobject_full_name,
if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
read_mverts(config, abc_mesh_data);
+ read_generated_coordinates(schema.getArbGeomParams(), config, selector);
}
if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
@@ -558,7 +559,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
/* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */
/* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
mesh->flag |= autosmooth;
}
@@ -868,7 +869,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr);
if (read_mesh != mesh) {
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
}
ISubDSchema::Sample sample;
diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc
index 5ca7022bb36..d428d98fdb9 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.cc
+++ b/source/blender/io/alembic/intern/abc_reader_object.cc
@@ -97,10 +97,6 @@ void AbcObjectReader::determine_inherits_xform()
}
}
-AbcObjectReader::~AbcObjectReader()
-{
-}
-
const IObject &AbcObjectReader::iobject() const
{
return m_iobject;
diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h
index 8e00ed42777..dacdcf3f722 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.h
+++ b/source/blender/io/alembic/intern/abc_reader_object.h
@@ -100,7 +100,7 @@ class AbcObjectReader {
public:
explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
- virtual ~AbcObjectReader();
+ virtual ~AbcObjectReader() = default;
const Alembic::Abc::IObject &iobject() const;
diff --git a/source/blender/io/collada/DocumentImporter.cpp b/source/blender/io/collada/DocumentImporter.cpp
index 214b5207a96..beadfc98c74 100644
--- a/source/blender/io/collada/DocumentImporter.cpp
+++ b/source/blender/io/collada/DocumentImporter.cpp
@@ -817,6 +817,8 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
matNode.set_ambient(ef->getAmbient());
matNode.set_specular(ef->getSpecular());
matNode.set_reflective(ef->getReflective());
+
+ matNode.update_material_nodetree();
}
/**
diff --git a/source/blender/io/collada/ErrorHandler.cpp b/source/blender/io/collada/ErrorHandler.cpp
index 844065e3ba3..86349e04ff6 100644
--- a/source/blender/io/collada/ErrorHandler.cpp
+++ b/source/blender/io/collada/ErrorHandler.cpp
@@ -36,11 +36,6 @@ ErrorHandler::ErrorHandler() : mError(false)
}
//--------------------------------------------------------------------
-ErrorHandler::~ErrorHandler()
-{
-}
-
-//--------------------------------------------------------------------
bool ErrorHandler::handleError(const COLLADASaxFWL::IError *error)
{
/* This method must return false when Collada should continue.
diff --git a/source/blender/io/collada/ErrorHandler.h b/source/blender/io/collada/ErrorHandler.h
index 1da17dbed42..f5fa9a4ad91 100644
--- a/source/blender/io/collada/ErrorHandler.h
+++ b/source/blender/io/collada/ErrorHandler.h
@@ -34,8 +34,6 @@ class ErrorHandler : public COLLADASaxFWL::IErrorHandler {
/** Constructor. */
ErrorHandler();
- /** Destructor. */
- virtual ~ErrorHandler();
/** handle any error thrown by the parser. */
bool virtual handleError(const COLLADASaxFWL::IError *error);
/** True if there was an error during parsing. */
diff --git a/source/blender/io/collada/ExtraHandler.cpp b/source/blender/io/collada/ExtraHandler.cpp
index 11cb75fb5e9..78785e9aead 100644
--- a/source/blender/io/collada/ExtraHandler.cpp
+++ b/source/blender/io/collada/ExtraHandler.cpp
@@ -30,10 +30,6 @@ ExtraHandler::ExtraHandler(DocumentImporter *dimp, AnimationImporter *aimp)
this->aimp = aimp;
}
-ExtraHandler::~ExtraHandler()
-{
-}
-
bool ExtraHandler::elementBegin(const char *elementName, const char **attributes)
{
/* \todo attribute handling for profile tags */
diff --git a/source/blender/io/collada/ExtraHandler.h b/source/blender/io/collada/ExtraHandler.h
index 4b13aaca2aa..98e30318aad 100644
--- a/source/blender/io/collada/ExtraHandler.h
+++ b/source/blender/io/collada/ExtraHandler.h
@@ -40,9 +40,6 @@ class ExtraHandler : public COLLADASaxFWL::IExtraDataCallbackHandler {
/** Constructor. */
ExtraHandler(DocumentImporter *dimp, AnimationImporter *aimp);
- /** Destructor. */
- virtual ~ExtraHandler();
-
/** Handle the beginning of an element. */
bool elementBegin(const char *elementName, const char **attributes);
diff --git a/source/blender/io/collada/ExtraTags.cpp b/source/blender/io/collada/ExtraTags.cpp
index 8c63a21f88a..f38b0d769d7 100644
--- a/source/blender/io/collada/ExtraTags.cpp
+++ b/source/blender/io/collada/ExtraTags.cpp
@@ -32,9 +32,7 @@ ExtraTags::ExtraTags(std::string profile)
this->tags = std::map<std::string, std::string>();
}
-ExtraTags::~ExtraTags()
-{
-}
+ExtraTags::~ExtraTags() = default;
bool ExtraTags::isProfile(std::string profile)
{
diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp
index 6ba31599fcd..ac4c65464c8 100644
--- a/source/blender/io/collada/Materials.cpp
+++ b/source/blender/io/collada/Materials.cpp
@@ -25,8 +25,6 @@ MaterialNode::MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map
shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
add_link(shader_node, 0, output_node, 0);
-
- ntreeUpdateTree(CTX_data_main(C), ntree);
}
}
@@ -61,8 +59,6 @@ MaterialNode::MaterialNode(bContext *C,
shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
add_link(shader_node, 0, output_node, 0);
-
- ntreeUpdateTree(CTX_data_main(C), ntree);
#endif
}
@@ -109,6 +105,11 @@ bNodeTree *MaterialNode::prepare_material_nodetree()
return ntree;
}
+void MaterialNode::update_material_nodetree()
+{
+ ntreeUpdateTree(CTX_data_main(mContext), ntree);
+}
+
bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label)
{
bNode *node = nodeAddStaticNode(mContext, ntree, node_type);
@@ -132,6 +133,19 @@ void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, in
nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
}
+void MaterialNode::add_link(bNode *from_node,
+ const char *from_label,
+ bNode *to_node,
+ const char *to_label)
+{
+ bNodeSocket *from_socket = nodeFindSocket(from_node, SOCK_OUT, from_label);
+ bNodeSocket *to_socket = nodeFindSocket(to_node, SOCK_IN, to_label);
+
+ if (from_socket && to_socket) {
+ nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
+ }
+}
+
void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val)
{
float reflectivity = val.getFloatValue();
@@ -217,22 +231,32 @@ void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot)
{
int locy = -300 * (node_map.size() - 2);
- if (cot.isColor()) {
- COLLADAFW::Color col = cot.getColor();
- bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
- float *fcol = (float *)socket->default_value;
- fcol[0] = material->r = col.getRed();
- fcol[1] = material->g = col.getGreen();
- fcol[2] = material->b = col.getBlue();
- fcol[3] = material->a = col.getAlpha();
- }
- else if (cot.isTexture()) {
+ if (cot.isTexture()) {
bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
if (texture_node != nullptr) {
add_link(texture_node, 0, shader_node, 0);
}
}
+ else {
+ bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
+ float *fcol = (float *)socket->default_value;
+
+ if (cot.isColor()) {
+ COLLADAFW::Color col = cot.getColor();
+ fcol[0] = material->r = col.getRed();
+ fcol[1] = material->g = col.getGreen();
+ fcol[2] = material->b = col.getBlue();
+ fcol[3] = material->a = col.getAlpha();
+ }
+ else {
+ /* no diffuse term = same as black */
+ fcol[0] = material->r = 0.0f;
+ fcol[1] = material->g = 0.0f;
+ fcol[2] = material->b = 0.0f;
+ fcol[3] = material->a = 1.0f;
+ }
+ }
}
Image *MaterialNode::get_diffuse_image()
@@ -325,7 +349,7 @@ void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot)
else if (cot.isTexture()) {
bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
if (texture_node != nullptr) {
- add_link(texture_node, 0, shader_node, 0);
+ add_link(texture_node, "Color", shader_node, "Emission");
}
}
@@ -362,18 +386,38 @@ void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot)
void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
{
+ bool has_specularity = true;
int locy = -300 * (node_map.size() - 2);
if (cot.isColor()) {
COLLADAFW::Color col = cot.getColor();
- bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
- set_color(node, col);
- /* TODO: Connect node */
+
+ if (col.getRed() == 0 && col.getGreen() == 0 && col.getBlue() == 0) {
+ has_specularity = false;
+ }
+ else {
+ bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
+ set_color(node, col);
+ /* TODO: Connect node */
+ }
}
- /* texture */
else if (cot.isTexture()) {
add_texture_node(cot, -300, locy, "Specular");
/* TODO: Connect node */
}
+ else {
+ /* no specular term) */
+ has_specularity = false;
+ }
+
+ if (!has_specularity) {
+ /* If specularity is black or not defined reset the Specular value to 0
+ TODO: This is a solution only for a corner case. We must find a better
+ way to handle specularity in general. Also note that currently we
+ do not export specularity values, see EffectExporter::operator()
+ */
+ bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Specular");
+ ((bNodeSocketValueFloat *)socket->default_value)->value = 0.0f;
+ }
}
bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot,
diff --git a/source/blender/io/collada/Materials.h b/source/blender/io/collada/Materials.h
index f671a00758d..1886acb7a6a 100644
--- a/source/blender/io/collada/Materials.h
+++ b/source/blender/io/collada/Materials.h
@@ -48,6 +48,7 @@ class MaterialNode {
bNodeTree *prepare_material_nodetree();
bNode *add_node(int node_type, int locx, int locy, std::string label);
void add_link(bNode *from_node, int from_index, bNode *to_node, int to_index);
+ void add_link(bNode *from_node, const char *from_label, bNode *to_node, const char *to_label);
bNode *add_texture_node(COLLADAFW::ColorOrTexture &cot, int locx, int locy, std::string label);
void setShaderType();
@@ -68,4 +69,6 @@ class MaterialNode {
void set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
COLLADAFW::ColorOrTexture &cot,
COLLADAFW::FloatOrParam &val);
+
+ void update_material_nodetree();
};
diff --git a/source/blender/io/collada/SkinInfo.cpp b/source/blender/io/collada/SkinInfo.cpp
index 8f6f1e467d9..12dee388a58 100644
--- a/source/blender/io/collada/SkinInfo.cpp
+++ b/source/blender/io/collada/SkinInfo.cpp
@@ -55,10 +55,7 @@ template<class T> static const char *bc_get_joint_name(T *node)
/* This is used to store data passed in write_controller_data.
* Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members
* so that arrays don't get freed until we free them explicitly. */
-SkinInfo::SkinInfo()
-{
- /* pass */
-}
+SkinInfo::SkinInfo() = default;
SkinInfo::SkinInfo(const SkinInfo &skin)
: weights(skin.weights),
diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index 300c555ac8f..0bebc4384a9 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -123,7 +123,7 @@ struct HierarchyContext {
*/
class AbstractHierarchyWriter {
public:
- virtual ~AbstractHierarchyWriter();
+ virtual ~AbstractHierarchyWriter() = default;
virtual void write(HierarchyContext &context) = 0;
/* TODO(Sybren): add function like absent() that's called when a writer was previously created,
* but wasn't used while exporting the current frame (for example, a particle-instanced mesh of
@@ -186,9 +186,6 @@ class ObjectIdentifier {
ObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id);
public:
- ObjectIdentifier(const ObjectIdentifier &other);
- ~ObjectIdentifier();
-
static ObjectIdentifier for_graph_root();
static ObjectIdentifier for_real_object(Object *object);
static ObjectIdentifier for_hierarchy_context(const HierarchyContext *context);
diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
index a33d636500f..3cda4d125d0 100644
--- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
@@ -137,10 +137,6 @@ AbstractHierarchyWriter *EnsuredWriter::operator->()
return writer_;
}
-AbstractHierarchyWriter::~AbstractHierarchyWriter()
-{
-}
-
bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context) const
{
const Object *object = context.object;
diff --git a/source/blender/io/common/intern/dupli_parent_finder.cc b/source/blender/io/common/intern/dupli_parent_finder.cc
index 73e33eff164..221e70ed587 100644
--- a/source/blender/io/common/intern/dupli_parent_finder.cc
+++ b/source/blender/io/common/intern/dupli_parent_finder.cc
@@ -25,14 +25,6 @@
namespace blender::io {
-DupliParentFinder::DupliParentFinder()
-{
-}
-
-DupliParentFinder::~DupliParentFinder()
-{
-}
-
void DupliParentFinder::insert(const DupliObject *dupli_ob)
{
dupli_set_.insert(dupli_ob->ob);
diff --git a/source/blender/io/common/intern/dupli_parent_finder.hh b/source/blender/io/common/intern/dupli_parent_finder.hh
index 3dcf037bb5e..b7632a72222 100644
--- a/source/blender/io/common/intern/dupli_parent_finder.hh
+++ b/source/blender/io/common/intern/dupli_parent_finder.hh
@@ -43,9 +43,6 @@ class DupliParentFinder final {
InstancerPIDToDuplisMap instancer_pid_to_duplis_;
public:
- DupliParentFinder();
- ~DupliParentFinder();
-
void insert(const DupliObject *dupli_ob);
bool is_duplicated(const Object *object) const;
diff --git a/source/blender/io/common/intern/object_identifier.cc b/source/blender/io/common/intern/object_identifier.cc
index 5d0b89b0630..2df1befcd69 100644
--- a/source/blender/io/common/intern/object_identifier.cc
+++ b/source/blender/io/common/intern/object_identifier.cc
@@ -35,15 +35,6 @@ ObjectIdentifier::ObjectIdentifier(Object *object,
{
}
-ObjectIdentifier::ObjectIdentifier(const ObjectIdentifier &other)
- : object(other.object), duplicated_by(other.duplicated_by), persistent_id(other.persistent_id)
-{
-}
-
-ObjectIdentifier::~ObjectIdentifier()
-{
-}
-
ObjectIdentifier ObjectIdentifier::for_real_object(Object *object)
{
return ObjectIdentifier(object, nullptr, PersistentID());
diff --git a/source/blender/io/gpencil/CMakeLists.txt b/source/blender/io/gpencil/CMakeLists.txt
index 11c9affbe5a..fec95be6aa8 100644
--- a/source/blender/io/gpencil/CMakeLists.txt
+++ b/source/blender/io/gpencil/CMakeLists.txt
@@ -39,22 +39,19 @@ set(INC_SYS
)
set(SRC
- intern/gpencil_io_capi.cc
+ intern/gpencil_io_base.cc
+ intern/gpencil_io_capi.cc
+ intern/gpencil_io_import_base.cc
+ intern/gpencil_io_import_svg.cc
- # This line must be removed if NanoSVG is moved to extern
- nanosvg/nanosvg.h
+ # This line must be removed if NanoSVG is moved to extern
+ nanosvg/nanosvg.h
- gpencil_io.h
-
- intern/gpencil_io_base.h
- intern/gpencil_io_base.cc
-
- intern/gpencil_io_import_base.h
- intern/gpencil_io_import_svg.h
- intern/gpencil_io_import_base.cc
- intern/gpencil_io_import_svg.cc
-
- intern/gpencil_io_export_base.h
+ gpencil_io.h
+ intern/gpencil_io_base.hh
+ intern/gpencil_io_export_base.hh
+ intern/gpencil_io_import_base.hh
+ intern/gpencil_io_import_svg.hh
)
set(LIB
@@ -65,8 +62,9 @@ set(LIB
if(WITH_PUGIXML)
list(APPEND SRC
- intern/gpencil_io_export_svg.h
intern/gpencil_io_export_svg.cc
+
+ intern/gpencil_io_export_svg.hh
)
list(APPEND INC_SYS
${PUGIXML_INCLUDE_DIR}
@@ -79,8 +77,9 @@ endif()
if(WITH_HARU)
list(APPEND SRC
- intern/gpencil_io_export_pdf.h
intern/gpencil_io_export_pdf.cc
+
+ intern/gpencil_io_export_pdf.hh
)
list(APPEND INC_SYS
${HARU_INCLUDE_DIRS}
diff --git a/source/blender/io/gpencil/gpencil_io.h b/source/blender/io/gpencil/gpencil_io.h
index f4b2e59f8c5..24b13479359 100644
--- a/source/blender/io/gpencil/gpencil_io.h
+++ b/source/blender/io/gpencil/gpencil_io.h
@@ -78,7 +78,7 @@ typedef enum eGpencilExportSelect {
GP_EXPORT_VISIBLE = 2,
} eGpencilExportSelect;
-/* Framerange to be exported. */
+/** Frame-range to be exported. */
typedef enum eGpencilExportFrame {
GP_EXPORT_FRAME_ACTIVE = 0,
GP_EXPORT_FRAME_SELECTED = 1,
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc
index 855252e648c..a2c1b8f5af6 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc
@@ -41,6 +41,7 @@
#include "BKE_gpencil_geom.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_scene.h"
#include "UI_view2d.h"
@@ -49,7 +50,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
-#include "gpencil_io_base.h"
+#include "gpencil_io_base.hh"
using blender::Span;
@@ -69,7 +70,21 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams)
cfra_ = iparams->frame_cur;
/* Calculate camera matrix. */
- Object *cam_ob = params_.v3d->camera;
+ prepare_camera_params(scene_, iparams);
+}
+
+void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *iparams)
+{
+ params_ = *iparams;
+ const bool is_pdf = params_.mode == GP_EXPORT_TO_PDF;
+ const bool any_camera = (params_.v3d->camera != nullptr);
+ const bool force_camera_view = is_pdf && any_camera;
+
+ /* Ensure camera switch is applied. */
+ BKE_scene_camera_switch_update(scene);
+
+ /* Calculate camera matrix. */
+ Object *cam_ob = scene->camera;
if (cam_ob != nullptr) {
/* Set up parameters. */
CameraParams params;
@@ -85,16 +100,18 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams)
invert_m4_m4(viewmat, cam_ob->obmat);
mul_m4_m4m4(persmat_, params.winmat, viewmat);
+ is_ortho_ = params.is_ortho;
}
else {
unit_m4(persmat_);
+ is_ortho_ = false;
}
winx_ = params_.region->winx;
winy_ = params_.region->winy;
/* Camera rectangle. */
- if (rv3d_->persp == RV3D_CAMOB) {
+ if ((rv3d_->persp == RV3D_CAMOB) || (force_camera_view)) {
render_x_ = (scene_->r.xsch * scene_->r.size) / 100;
render_y_ = (scene_->r.ysch * scene_->r.size) / 100;
@@ -112,11 +129,14 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams)
}
else {
is_camera_ = false;
+ is_ortho_ = false;
/* Calc selected object boundbox. Need set initial value to some variables. */
camera_ratio_ = 1.0f;
offset_.x = 0.0f;
offset_.y = 0.0f;
+ create_object_list();
+
selected_objects_boundbox_calc();
rctf boundbox;
selected_objects_boundbox_get(&boundbox);
@@ -228,13 +248,15 @@ bool GpencilIO::gpencil_3D_point_to_screen_space(const float3 co, float2 &r_co)
}
/** Convert to render space. */
-float2 GpencilIO::gpencil_3D_point_to_render_space(const float3 co)
+float2 GpencilIO::gpencil_3D_point_to_render_space(const float3 co, const bool is_ortho)
{
float3 parent_co = diff_mat_ * co;
mul_m4_v3(persmat_, parent_co);
- parent_co.x = parent_co.x / max_ff(FLT_MIN, parent_co[2]);
- parent_co.y = parent_co.y / max_ff(FLT_MIN, parent_co[2]);
+ if (!is_ortho) {
+ parent_co.x = parent_co.x / max_ff(FLT_MIN, parent_co.z);
+ parent_co.y = parent_co.y / max_ff(FLT_MIN, parent_co.z);
+ }
float2 r_co;
r_co.x = (parent_co.x + 1.0f) / 2.0f * (float)render_x_;
@@ -257,7 +279,7 @@ float2 GpencilIO::gpencil_3D_point_to_2D(const float3 co)
{
const bool is_camera = (bool)(rv3d_->persp == RV3D_CAMOB);
if (is_camera) {
- return gpencil_3D_point_to_render_space(co);
+ return gpencil_3D_point_to_render_space(co, is_orthographic());
}
float2 result;
gpencil_3D_point_to_screen_space(co, result);
@@ -296,7 +318,7 @@ void GpencilIO::prepare_stroke_export_colors(Object *ob, bGPDstroke *gps)
/* Stroke color. */
copy_v4_v4(stroke_color_, gp_style->stroke_rgba);
- avg_opacity_ = 0;
+ avg_opacity_ = 0.0f;
/* Get average vertex color and apply. */
float avg_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) {
@@ -324,6 +346,11 @@ bool GpencilIO::is_camera_mode()
return is_camera_;
}
+bool GpencilIO::is_orthographic()
+{
+ return is_ortho_;
+}
+
/* Calculate selected strokes boundbox. */
void GpencilIO::selected_objects_boundbox_calc()
{
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.h b/source/blender/io/gpencil/intern/gpencil_io_base.hh
index 986221618b7..c3c6f1156bb 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh
@@ -50,6 +50,7 @@ class GpencilIO {
GpencilIO(const GpencilIOParams *iparams);
void frame_number_set(const int value);
+ void prepare_camera_params(Scene *scene, const GpencilIOParams *iparams);
protected:
GpencilIOParams params_;
@@ -87,13 +88,14 @@ class GpencilIO {
/* Geometry functions. */
bool gpencil_3D_point_to_screen_space(const float3 co, float2 &r_co);
- float2 gpencil_3D_point_to_render_space(const float3 co);
+ float2 gpencil_3D_point_to_render_space(const float3 co, const bool is_ortho);
float2 gpencil_3D_point_to_2D(const float3 co);
float stroke_point_radius_get(struct bGPDlayer *gpl, struct bGPDstroke *gps);
void create_object_list();
bool is_camera_mode();
+ bool is_orthographic();
float stroke_average_opacity_get();
@@ -107,6 +109,7 @@ class GpencilIO {
private:
float avg_opacity_;
bool is_camera_;
+ bool is_ortho_;
rctf select_boundbox_;
/* Camera matrix. */
diff --git a/source/blender/io/gpencil/intern/gpencil_io_capi.cc b/source/blender/io/gpencil/intern/gpencil_io_capi.cc
index 231d23948ef..544c51e0b4f 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_capi.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_capi.cc
@@ -21,7 +21,7 @@
* \ingroup bgpencil
*/
-#include <stdio.h>
+#include <cstdio>
#include "BLI_listbase.h"
@@ -40,14 +40,14 @@
#include "../gpencil_io.h"
#ifdef WITH_HARU
-# include "gpencil_io_export_pdf.h"
+# include "gpencil_io_export_pdf.hh"
#endif
#ifdef WITH_PUGIXML
-# include "gpencil_io_export_svg.h"
+# include "gpencil_io_export_svg.hh"
#endif
-#include "gpencil_io_import_svg.h"
+#include "gpencil_io_import_svg.hh"
#ifdef WITH_HARU
using blender::io::gpencil::GpencilExporterPDF;
@@ -121,6 +121,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph,
CFRA = i;
BKE_scene_graph_update_for_newframe(depsgraph);
+ exporter->prepare_camera_params(scene, iparams);
exporter->frame_number_set(i);
exporter->add_newpage();
exporter->add_body();
@@ -129,9 +130,11 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph,
/* Back to original frame. */
exporter->frame_number_set(iparams->frame_cur);
CFRA = iparams->frame_cur;
+ BKE_scene_camera_switch_update(scene);
BKE_scene_graph_update_for_newframe(depsgraph);
}
else {
+ exporter->prepare_camera_params(scene, iparams);
exporter->add_newpage();
exporter->add_body();
result = exporter->write();
@@ -144,6 +147,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph,
/* Export current frame in SVG. */
#ifdef WITH_PUGIXML
static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter,
+ Scene *scene,
const GpencilIOParams *iparams,
const bool newpage,
const bool body,
@@ -151,6 +155,8 @@ static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter,
{
bool result = false;
exporter->frame_number_set(iparams->frame_cur);
+ exporter->prepare_camera_params(scene, iparams);
+
if (newpage) {
result |= exporter->add_newpage();
}
@@ -185,7 +191,7 @@ bool gpencil_io_export(const char *filename, GpencilIOParams *iparams)
#ifdef WITH_PUGIXML
case GP_EXPORT_TO_SVG: {
GpencilExporterSVG exporter = GpencilExporterSVG(filename, iparams);
- return gpencil_io_export_frame_svg(&exporter, iparams, true, true, true);
+ return gpencil_io_export_frame_svg(&exporter, scene_, iparams, true, true, true);
break;
}
#endif
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_base.h b/source/blender/io/gpencil/intern/gpencil_io_export_base.hh
index 19a24a75fd2..ffb1c6ce262 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_base.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_base.hh
@@ -21,7 +21,7 @@
/** \file
* \ingroup bgpencil
*/
-#include "gpencil_io_base.h"
+#include "gpencil_io_base.hh"
namespace blender::io::gpencil {
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
index ba16d635c2d..0f90855dcb8 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
@@ -49,7 +49,7 @@
#include "UI_view2d.h"
#include "gpencil_io.h"
-#include "gpencil_io_export_pdf.h"
+#include "gpencil_io_export_pdf.hh"
namespace blender ::io ::gpencil {
@@ -69,7 +69,6 @@ GpencilExporterPDF::GpencilExporterPDF(const char *filename, const GpencilIOPara
pdf_ = nullptr;
page_ = nullptr;
- gstate_ = nullptr;
}
bool GpencilExporterPDF::new_document()
@@ -169,16 +168,29 @@ void GpencilExporterPDF::export_gpencil_layers()
if (!ED_gpencil_stroke_material_visible(ob, gps)) {
continue;
}
- /* Duplicate the stroke to apply any layer thickness change. */
- bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob,
- gps_duplicate->mat_nr + 1);
+ /* Skip invisible lines. */
+ prepare_stroke_export_colors(ob, gps);
+ const float fill_opacity = fill_color_[3] * gpl->opacity;
+ const float stroke_opacity = stroke_color_[3] * stroke_average_opacity_get() *
+ gpl->opacity;
+ if ((fill_opacity < GPENCIL_ALPHA_OPACITY_THRESH) &&
+ (stroke_opacity < GPENCIL_ALPHA_OPACITY_THRESH)) {
+ continue;
+ }
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) &&
- (gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH));
+ (gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
+ (stroke_opacity > GPENCIL_ALPHA_OPACITY_THRESH));
const bool is_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) &&
(gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH));
- prepare_stroke_export_colors(ob, gps_duplicate);
+
+ if ((!is_stroke) && (!is_fill)) {
+ continue;
+ }
+
+ /* Duplicate the stroke to apply any layer thickness change. */
+ bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
/* Apply layer thickness change. */
gps_duplicate->thickness += gpl->line_change;
@@ -283,29 +295,35 @@ void GpencilExporterPDF::color_set(bGPDlayer *gpl, const bool do_fill)
{
const float fill_opacity = fill_color_[3] * gpl->opacity;
const float stroke_opacity = stroke_color_[3] * stroke_average_opacity_get() * gpl->opacity;
+ const bool need_state = (do_fill && fill_opacity < 1.0f) || (stroke_opacity < 1.0f);
HPDF_Page_GSave(page_);
- gstate_ = HPDF_CreateExtGState(pdf_);
+ HPDF_ExtGState gstate = (need_state) ? HPDF_CreateExtGState(pdf_) : nullptr;
float col[3];
if (do_fill) {
interp_v3_v3v3(col, fill_color_, gpl->tintcolor, gpl->tintcolor[3]);
linearrgb_to_srgb_v3_v3(col, col);
CLAMP3(col, 0.0f, 1.0f);
-
- HPDF_ExtGState_SetAlphaFill(gstate_, clamp_f(fill_opacity, 0.0f, 1.0f));
HPDF_Page_SetRGBFill(page_, col[0], col[1], col[2]);
+ if (gstate) {
+ HPDF_ExtGState_SetAlphaFill(gstate, clamp_f(fill_opacity, 0.0f, 1.0f));
+ }
}
else {
interp_v3_v3v3(col, stroke_color_, gpl->tintcolor, gpl->tintcolor[3]);
linearrgb_to_srgb_v3_v3(col, col);
CLAMP3(col, 0.0f, 1.0f);
- HPDF_ExtGState_SetAlphaFill(gstate_, clamp_f(stroke_opacity, 0.0f, 1.0f));
- HPDF_ExtGState_SetAlphaStroke(gstate_, clamp_f(stroke_opacity, 0.0f, 1.0f));
HPDF_Page_SetRGBFill(page_, col[0], col[1], col[2]);
HPDF_Page_SetRGBStroke(page_, col[0], col[1], col[2]);
+ if (gstate) {
+ HPDF_ExtGState_SetAlphaFill(gstate, clamp_f(stroke_opacity, 0.0f, 1.0f));
+ HPDF_ExtGState_SetAlphaStroke(gstate, clamp_f(stroke_opacity, 0.0f, 1.0f));
+ }
+ }
+ if (gstate) {
+ HPDF_Page_SetExtGState(page_, gstate);
}
- HPDF_Page_SetExtGState(page_, gstate_);
}
} // namespace blender::io::gpencil
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.h b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh
index 009c05a8b49..89d97f79783 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh
@@ -22,7 +22,7 @@
* \ingroup bgpencil
*/
-#include "gpencil_io_export_base.h"
+#include "gpencil_io_export_base.hh"
#include "hpdf.h"
struct GpencilIOParams;
@@ -49,8 +49,6 @@ class GpencilExporterPDF : public GpencilExporter {
HPDF_Doc pdf_;
/* PDF page. */
HPDF_Page page_;
- /* State. */
- HPDF_ExtGState gstate_;
bool create_document();
bool add_page();
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
index 89584cd242f..c62764cca06 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
@@ -49,7 +49,7 @@
#include "UI_view2d.h"
#include "gpencil_io.h"
-#include "gpencil_io_export_svg.h"
+#include "gpencil_io_export_svg.hh"
#include "pugixml.hpp"
@@ -119,6 +119,7 @@ void GpencilExporterSVG::create_document_header()
main_node_.append_attribute("version").set_value("1.0");
main_node_.append_attribute("x").set_value("0px");
main_node_.append_attribute("y").set_value("0px");
+ main_node_.append_attribute("xmlns").set_value("http://www.w3.org/2000/svg");
std::string width;
std::string height;
@@ -396,7 +397,7 @@ void GpencilExporterSVG::color_string_set(bGPDlayer *gpl,
* \param node: Parent node
* \param x: X location
* \param y: Y location
- * \param width: width of the recntagle
+ * \param width: width of the rectangle
* \param height: Height of the rectangle
* \param thickness: Thickness of the line
* \param hexcolor: Color of the line
@@ -427,7 +428,7 @@ void GpencilExporterSVG::add_rect(pugi::xml_node node,
* \param x: X location
* \param y: Y location
* \param text: Text to include
- * \param size: Size of th etext
+ * \param size: Size of the text
* \param hexcolor: Color of the text
*/
void GpencilExporterSVG::add_text(pugi::xml_node node,
@@ -448,7 +449,7 @@ void GpencilExporterSVG::add_text(pugi::xml_node node,
}
/** Convert a color to Hex value (#FFFFFF). */
-std::string GpencilExporterSVG::rgb_to_hexstr(float color[3])
+std::string GpencilExporterSVG::rgb_to_hexstr(const float color[3])
{
uint8_t r = color[0] * 255.0f;
uint8_t g = color[1] * 255.0f;
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.h b/source/blender/io/gpencil/intern/gpencil_io_export_svg.hh
index f564736c16e..3bff31f20bf 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.hh
@@ -23,7 +23,7 @@
*/
#include "BLI_path_util.h"
-#include "gpencil_io_export_base.h"
+#include "gpencil_io_export_base.hh"
#include "pugixml.hpp"
struct GpencilIOParams;
@@ -70,20 +70,20 @@ class GpencilExporterSVG : public GpencilExporter {
void export_stroke_to_path(struct bGPDlayer *gpl,
struct bGPDstroke *gps,
pugi::xml_node node_gpl,
- const bool is_fill);
+ const bool do_fill);
void export_stroke_to_polyline(struct bGPDlayer *gpl,
struct bGPDstroke *gps,
pugi::xml_node node_gpl,
const bool is_stroke,
- const bool is_fill);
+ const bool do_fill);
void color_string_set(struct bGPDlayer *gpl,
struct bGPDstroke *gps,
pugi::xml_node node_gps,
- const bool is_fill);
+ const bool do_fill);
- std::string rgb_to_hexstr(float color[3]);
+ std::string rgb_to_hexstr(const float color[3]);
};
} // namespace blender::io::gpencil
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
index 2e7cfdeb5cd..d0b6e20bda2 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
@@ -33,7 +33,7 @@
#include "ED_gpencil.h"
-#include "gpencil_io_import_base.h"
+#include "gpencil_io_import_base.hh"
namespace blender::io::gpencil {
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_base.h b/source/blender/io/gpencil/intern/gpencil_io_import_base.hh
index efe6264e4e9..7d6fad2340b 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_base.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_base.hh
@@ -21,7 +21,7 @@
/** \file
* \ingroup bgpencil
*/
-#include "gpencil_io_base.h"
+#include "gpencil_io_base.hh"
namespace blender::io::gpencil {
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
index 7f450477ac2..52fcc017ffb 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
@@ -36,7 +36,7 @@
#include "ED_gpencil.h"
#include "gpencil_io.h"
-#include "gpencil_io_import_svg.h"
+#include "gpencil_io_import_svg.hh"
/* Custom flags for NanoSVG. */
#define NANOSVG_ALL_COLOR_KEYWORDS
@@ -69,9 +69,7 @@ bool GpencilImporterSVG::read()
params_.ob = create_object();
if (params_.ob == nullptr) {
std::cout << "Unable to create new object.\n";
- if (svg_data) {
- nsvgDelete(svg_data);
- }
+ nsvgDelete(svg_data);
return false;
}
@@ -102,7 +100,7 @@ bool GpencilImporterSVG::read()
bGPDlayer *gpl = (bGPDlayer *)BLI_findstring(
&gpd_->layers, layer_id, offsetof(bGPDlayer, info));
if (gpl == nullptr) {
- gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true);
+ gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true, false);
/* Disable lights. */
gpl->flag &= ~GP_LAYER_USE_LIGHTS;
}
@@ -118,15 +116,17 @@ bool GpencilImporterSVG::read()
}
/* Create_shape materials. */
- const char *const mat_names[] = {"Stroke", "Fill"};
+ const char *const mat_names[] = {"Stroke", "Fill", "Both"};
int index = 0;
- if ((is_stroke) && (is_fill)) {
+ if ((is_stroke) && (!is_fill)) {
index = 0;
- is_fill = false;
}
else if ((!is_stroke) && (is_fill)) {
index = 1;
}
+ else if ((is_stroke) && (is_fill)) {
+ index = 2;
+ }
int32_t mat_index = create_material(mat_names[index], is_stroke, is_fill);
/* Loop all paths to create the stroke data. */
@@ -196,10 +196,10 @@ void GpencilImporterSVG::create_stroke(bGPdata *gpd,
pt->strength = shape->opacity;
pt->pressure = 1.0f;
pt->z = 0.0f;
- /* TODO: (antoniov) Can be improved loading curve data instead of loading strokes. */
+ /* TODO(antoniov): Can be improved loading curve data instead of loading strokes. */
interp_v2_v2v2v2v2_cubic(&pt->x, &p[0], &p[2], &p[4], &p[6], a);
- /* Scale from milimeters. */
+ /* Scale from millimeters. */
mul_v3_fl(&pt->x, 0.001f);
mul_m4_v3(matrix, &pt->x);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.h b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh
index 6a34ec8423b..0e9271dd2c6 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh
@@ -21,7 +21,7 @@
/** \file
* \ingroup bgpencil
*/
-#include "gpencil_io_import_base.h"
+#include "gpencil_io_import_base.hh"
struct GpencilIOParams;
struct NSVGshape;
diff --git a/source/blender/io/gpencil/nanosvg/nanosvg.h b/source/blender/io/gpencil/nanosvg/nanosvg.h
index 1009d684f7c..94dad37861a 100644
--- a/source/blender/io/gpencil/nanosvg/nanosvg.h
+++ b/source/blender/io/gpencil/nanosvg/nanosvg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
+ * Copyright (c) 2013-14 `Mikko Mononen <memon@inside.org>`
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
@@ -459,9 +459,9 @@ typedef struct NSVGparser {
float dpi;
char pathFlag;
char defsFlag;
- /** Blender breadscrum for layers. */
+ /** Blender breadcrumb for layers. */
char breadcrumb[NSVG_MAX_BREADCRUMB][64];
- /** Blender number of elements in breadscrum. */
+ /** Blender number of elements in breadcrumb. */
int breadcrumb_len;
} NSVGparser;
@@ -1289,7 +1289,7 @@ static const char *nsvg__parseNumber(const char *s, char *it, const int size)
return s;
}
-static const char *nsvg__getNextPathItem(const char *s, char *it)
+static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs)
{
it[0] = '\0';
// Skip white spaces and commas
@@ -1297,6 +1297,15 @@ static const char *nsvg__getNextPathItem(const char *s, char *it)
s++;
if (!*s)
return s;
+
+ /* Blender: Special case for arc command's 4th and 5th arguments. */
+ if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) {
+ it[0] = s[0];
+ it[1] = '\0';
+ s++;
+ return s;
+ }
+
if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
s = nsvg__parseNumber(s, it, 64);
}
@@ -1576,8 +1585,8 @@ static int nsvg__isCoordinate(const char *s)
// optional sign
if (*s == '-' || *s == '+')
s++;
- // must have at least one digit
- return nsvg__isdigit(*s);
+ // must have at least one digit, or start by a dot
+ return (nsvg__isdigit(*s) || *s == '.');
}
static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str)
@@ -2353,7 +2362,12 @@ static void nsvg__pathArcTo(NSVGparser *p, float *cpx, float *cpy, float *args,
// The loop assumes an iteration per end point (including start and end), this +1.
ndivs = (int)(fabsf(da) / (NSVG_PI * 0.5f) + 1.0f);
hda = (da / (float)ndivs) / 2.0f;
- kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
+ // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite)
+ if ((hda < 1e-3f) && (hda > -1e-3f))
+ hda *= 0.5f;
+ else
+ hda = (1.0f - cosf(hda)) / sinf(hda);
+ kappa = fabsf(4.0f / 3.0f * hda);
if (da < 0.0f)
kappa = -kappa;
@@ -2413,7 +2427,7 @@ static void nsvg__parsePath(NSVGparser *p, const char **attr)
nargs = 0;
while (*s) {
- s = nsvg__getNextPathItem(s, item);
+ s = nsvg__getNextPathItem(s, item, cmd, nargs);
if (!*item)
break;
if (cmd != '\0' && nsvg__isCoordinate(item)) {
@@ -2740,7 +2754,7 @@ static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag)
s = attr[i + 1];
nargs = 0;
while (*s) {
- s = nsvg__getNextPathItem(s, item);
+ s = nsvg__getNextPathItem(s, item, '\0', nargs);
args[nargs++] = (float)nsvg__atof(item);
if (nargs >= 2) {
if (npts == 0)
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc
index 694fc76a446..5e66136abf1 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.cc
+++ b/source/blender/io/usd/intern/usd_writer_abstract.cc
@@ -41,10 +41,6 @@ USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_contex
{
}
-USDAbstractWriter::~USDAbstractWriter()
-{
-}
-
bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
{
return true;
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index 2143164e3dd..6f143a7e241 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -49,7 +49,6 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
public:
USDAbstractWriter(const USDExporterContext &usd_export_context);
- virtual ~USDAbstractWriter();
virtual void write(HierarchyContext &context) override;
diff --git a/source/blender/io/usd/intern/usd_writer_metaball.cc b/source/blender/io/usd/intern/usd_writer_metaball.cc
index 8e32bd4705a..28c96c3a511 100644
--- a/source/blender/io/usd/intern/usd_writer_metaball.cc
+++ b/source/blender/io/usd/intern/usd_writer_metaball.cc
@@ -62,7 +62,7 @@ Mesh *USDMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
return mesh_eval;
}
r_needsfree = true;
- return BKE_mesh_new_from_object(usd_export_context_.depsgraph, object_eval, false);
+ return BKE_mesh_new_from_object(usd_export_context_.depsgraph, object_eval, false, false);
}
void USDMetaballWriter::free_export_mesh(Mesh *mesh)
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index d88db091cc2..dd262f78f6b 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -366,7 +366,7 @@ typedef struct Library {
struct PackedFile *packedfile;
- /* Temp data needed by read/write code. */
+ /* Temp data needed by read/write code, and liboverride recursive resync. */
int temp_index;
/** See BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION, needed for do_versions. */
short versionfile, subversionfile;
@@ -491,6 +491,11 @@ enum {
* Note that this also applies to shapekeys, even though they are not 100% embedded data...
*/
LIB_EMBEDDED_DATA_LIB_OVERRIDE = 1 << 12,
+ /**
+ * The override data-block appears to not be needed anymore after resync with linked data, but it
+ * was kept around (because e.g. detected as user-edited).
+ */
+ LIB_LIB_OVERRIDE_RESYNC_LEFTOVER = 1 << 13,
};
/**
@@ -550,8 +555,15 @@ enum {
/* RESET_AFTER_USE tag existing data before linking so we know what is new. */
LIB_TAG_PRE_EXISTING = 1 << 11,
- /* The data-block is a copy-on-write/localized version. */
+ /**
+ * The data-block is a copy-on-write/localized version.
+ *
+ * \warning This should not be cleared on existing data.
+ * If support for this is needed, see T88026 as this flag controls memory ownership
+ * of physics *shared* pointers.
+ */
LIB_TAG_COPIED_ON_WRITE = 1 << 12,
+
LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT = 1 << 13,
LIB_TAG_LOCALIZED = 1 << 14,
@@ -586,14 +598,16 @@ typedef enum IDRecalcFlag {
/* ** Object transformation changed. ** */
ID_RECALC_TRANSFORM = (1 << 0),
- /* ** Object geometry changed. **
+ /* ** Geometry changed. **
*
* When object of armature type gets tagged with this flag, its pose is
* re-evaluated.
* When object of other type is tagged with this flag it makes the modifier
* stack to be re-evaluated.
* When object data type (mesh, curve, ...) gets tagged with this flag it
- * makes all objects which shares this data-block to be updated. */
+ * makes all objects which shares this data-block to be updated.
+ * When a collection gets tagged with this flag, all objects depending on the geometry and
+ * transforms on any of the objects in the collection are updated. */
ID_RECALC_GEOMETRY = (1 << 1),
/* ** Animation or time changed and animation is to be re-evaluated. ** */
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 5cc525a6cff..583e56de9c2 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -265,9 +265,10 @@ typedef struct bPoseChannel {
* since the alternative is highly complicated - campbell
*/
struct bPoseChannel *custom_tx;
- float custom_scale;
-
- char _pad1[4];
+ float custom_scale; /* Deprecated */
+ float custom_scale_xyz[3];
+ float custom_translation[3];
+ float custom_rotation_euler[3];
/** Transforms - written in by actions or transform. */
float loc[3];
@@ -417,9 +418,9 @@ typedef enum ePchan_DrawFlag {
PCHAN_DRAW_NO_CUSTOM_BONE_SIZE = (1 << 0),
} ePchan_DrawFlag;
-#define PCHAN_CUSTOM_DRAW_SIZE(pchan) \
- (pchan)->custom_scale *( \
- ((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length)
+/* Note: It doesn't take custom_scale_xyz into account */
+#define PCHAN_CUSTOM_BONE_LENGTH(pchan) \
+ (((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length)
#ifdef DNA_DEPRECATED_ALLOW
/* PoseChannel->bboneflag */
diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h
index 09304ce09f2..85780bc33c5 100644
--- a/source/blender/makesdna/DNA_armature_types.h
+++ b/source/blender/makesdna/DNA_armature_types.h
@@ -134,7 +134,7 @@ typedef struct bArmature {
/** ID data is older than edit-mode data (TODO: move to edit-mode struct). */
char needs_flush_to_id;
- char _pad0[7];
+ char _pad0[3];
int flag;
int drawtype;
@@ -146,6 +146,9 @@ typedef struct bArmature {
unsigned int layer_used;
/** For buttons to work, both variables in this order together. */
unsigned int layer, layer_protected;
+
+ /** Relative position of the axes on the bone, from head (0.0f) to tail (1.0f). */
+ float axes_position;
} bArmature;
/* armature->flag */
@@ -230,8 +233,10 @@ typedef enum eBone_Flag {
BONE_MULT_VG_ENV = (1 << 11),
/** bone doesn't deform geometry */
BONE_NO_DEFORM = (1 << 12),
+#ifdef DNA_DEPRECATED_ALLOW
/** set to prevent destruction of its unkeyframed pose (after transform) */
BONE_UNKEYED = (1 << 13),
+#endif
/** set to prevent hinge child bones from influencing the transform center */
BONE_HINGE_CHILD_TRANSFORM = (1 << 14),
#ifdef DNA_DEPRECATED_ALLOW
diff --git a/source/blender/makesdna/DNA_boid_types.h b/source/blender/makesdna/DNA_boid_types.h
index 882d4eb1b3b..540446ccd9d 100644
--- a/source/blender/makesdna/DNA_boid_types.h
+++ b/source/blender/makesdna/DNA_boid_types.h
@@ -97,7 +97,8 @@ typedef struct BoidRuleFollowLeader {
} BoidRuleFollowLeader;
typedef struct BoidRuleAverageSpeed {
BoidRule rule;
- float wander, level, speed, rt;
+ float wander, level, speed;
+ char _pad0[4];
} BoidRuleAverageSpeed;
typedef struct BoidRuleFight {
BoidRule rule;
@@ -178,7 +179,7 @@ typedef struct BoidState {
//} BoidSignal;
// typedef struct BoidSignalDefine {
// struct BoidSignalDefine *next, *prev;
-// int id, rt;
+// int id, _pad[4];
// char name[32];
//} BoidSignalDefine;
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index a11e7d77c67..986c009ac26 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -133,7 +133,8 @@ typedef struct BrushGpencilSettings {
/** Factor to extend stroke extremes using fill tool. */
float fill_extend_fac;
- char _pad3[4];
+ /** Number of pixels to dilate fill area. */
+ int dilate_pixels;
struct CurveMapping *curve_sensitivity;
struct CurveMapping *curve_strength;
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 07fbf263d80..a94bc4625df 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -534,6 +534,8 @@ typedef struct bRotLimitConstraint {
float zmin, zmax;
short flag;
short flag2;
+ char euler_order;
+ char _pad[3];
} bRotLimitConstraint;
/* Limit Scale Constraint */
diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h
index 4f914089347..f6242679808 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -43,25 +43,7 @@ struct Key;
struct Material;
struct Object;
struct VFont;
-
-/* These two Lines with # tell makesdna this struct can be excluded. */
-#
-#
-typedef struct PathPoint {
- /** Grr, cant get rid of tilt yet. */
- float vec[4];
- float quat[4];
- float radius, weight;
-} PathPoint;
-
-/* These two Lines with # tell makesdna this struct can be excluded. */
-#
-#
-typedef struct Path {
- struct PathPoint *data;
- int len;
- float totdist;
-} Path;
+struct CurveEval;
/* These two Lines with # tell makesdna this struct can be excluded. */
#
@@ -72,7 +54,7 @@ typedef struct BevPoint {
float sina, cosa;
/** 3D Only. */
float dir[3], tan[3], quat[4];
- short split_tag, dupe_tag;
+ short dupe_tag;
} BevPoint;
/* These two Lines with # tell makesdna this struct can be excluded. */
@@ -319,6 +301,12 @@ typedef struct Curve {
char _pad2[6];
float fsize_realtime;
+ /**
+ * A pointer to curve data from geometry nodes, currently only set for evaluated
+ * objects by the dependency graph iterator, and owned by #geometry_set_eval.
+ */
+ struct CurveEval *curve_eval;
+
void *batch_cache;
} Curve;
@@ -347,7 +335,7 @@ enum {
CU_BACK = 1 << 2,
CU_PATH = 1 << 3,
CU_FOLLOW = 1 << 4,
- /* CU_UV_ORCO = 1 << 5, */ /* DEPRECATED */
+ CU_PATH_CLAMP = 1 << 5,
CU_DEFORM_BOUNDS_OFF = 1 << 6,
CU_STRETCH = 1 << 7,
/* CU_OFFS_PATHDIST = 1 << 8, */ /* DEPRECATED */
@@ -425,7 +413,6 @@ enum {
/* Nurb.flag */
enum {
CU_SMOOTH = 1 << 0,
- CU_2D = 1 << 3, /* moved from type since 2.4x */
};
/* Nurb.type */
diff --git a/source/blender/makesdna/DNA_effect_types.h b/source/blender/makesdna/DNA_effect_types.h
index 33f2e1b47c0..307a212a139 100644
--- a/source/blender/makesdna/DNA_effect_types.h
+++ b/source/blender/makesdna/DNA_effect_types.h
@@ -71,13 +71,15 @@ extern "C" {
typedef struct Effect {
struct Effect *next, *prev;
- short type, flag, buttype, rt;
+ short type, flag, buttype;
+ char _pad0[2];
} Effect;
typedef struct BuildEff {
struct BuildEff *next, *prev;
- short type, flag, buttype, rt;
+ short type, flag, buttype;
+ char _pad0[2];
float len, sfra;
@@ -88,7 +90,8 @@ typedef struct BuildEff {
typedef struct Particle {
float co[3], no[3];
float time, lifetime;
- short mat_nr, rt;
+ short mat_nr;
+ char _pad0[2];
} Particle;
struct Collection;
diff --git a/source/blender/makesdna/DNA_genfile.h b/source/blender/makesdna/DNA_genfile.h
index 6fcbc53d47b..74d668a1081 100644
--- a/source/blender/makesdna/DNA_genfile.h
+++ b/source/blender/makesdna/DNA_genfile.h
@@ -63,6 +63,7 @@ typedef enum eSDNA_Type {
#define SDNA_TYPE_VOID 9
SDNA_TYPE_INT64 = 10,
SDNA_TYPE_UINT64 = 11,
+ SDNA_TYPE_INT8 = 12,
} eSDNA_Type;
/**
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index 2478bbe88ba..a4ab38f6022 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -184,6 +184,8 @@
.layer_pass = 0, \
.hardeness = 1.0f, \
.curve_intensity = NULL, \
+ .fading_end = 10.0f, \
+ .fading_end_factor = 0.2f, \
}
#define _DNA_DEFAULT_SimplifyGpencilModifierData \
@@ -251,6 +253,8 @@
.thickness_fac = 1.0f, \
.thickness = 30, \
.layer_pass = 0, \
+ .fading_end = 10.0f, \
+ .fading_end_factor = 0.2f, \
}
#define _DNA_DEFAULT_TimeGpencilModifierData \
@@ -288,11 +292,21 @@
.edge_types = LRT_EDGE_FLAG_ALL_TYPE, \
.thickness = 25, \
.opacity = 1.0f, \
- .flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP | LRT_GPENCIL_SOFT_SELECTION, \
+ .flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \
.crease_threshold = DEG2RAD(140.0f), \
.calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES, \
.angle_splitting_threshold = DEG2RAD(60.0f), \
.chaining_image_threshold = 0.001f, \
}
+#define _DNA_DEFAULT_LengthGpencilModifierData \
+ { \
+ .start_fac = 0.1f,\
+ .end_fac = 0.1f,\
+ .overshoot_fac = 0.01f,\
+ .pass_index = 0,\
+ .material = NULL,\
+ }
+
+
/* clang-format off */
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index bc046a3b830..410212ce100 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -54,6 +54,7 @@ typedef enum GpencilModifierType {
eGpencilModifierType_Multiply = 17,
eGpencilModifierType_Texture = 18,
eGpencilModifierType_Lineart = 19,
+ eGpencilModifierType_Length = 20,
/* Keep last. */
NUM_GREASEPENCIL_MODIFIER_TYPES,
} GpencilModifierType;
@@ -187,7 +188,12 @@ typedef struct ThickGpencilModifierData {
int thickness;
/** Custom index for passes. */
int layer_pass;
- char _pad[4];
+ /** Start/end distances of the fading effect. */
+ float fading_start;
+ float fading_end;
+ float fading_end_factor;
+ /** Fading reference object */
+ struct Object *object;
struct CurveMapping *curve_thickness;
} ThickGpencilModifierData;
@@ -199,6 +205,7 @@ typedef enum eThickGpencil_Flag {
GP_THICK_NORMALIZE = (1 << 4),
GP_THICK_INVERT_LAYERPASS = (1 << 5),
GP_THICK_INVERT_MATERIAL = (1 << 6),
+ GP_THICK_FADING = (1 << 7),
} eThickGpencil_Flag;
typedef struct TimeGpencilModifierData {
@@ -291,9 +298,16 @@ typedef struct OpacityGpencilModifierData {
int flag;
/** Main Opacity factor. */
float factor;
+ /** Fading controlling object */
+ int _pad0;
+ struct Object *object;
+ /** Start/end distances of the fading effect. */
+ float fading_start;
+ float fading_end;
+ float fading_end_factor;
/** Modify stroke, fill or both. */
char modify_color;
- char _pad[3];
+ char _pad1[3];
/** Custom index for passes. */
int layer_pass;
@@ -309,6 +323,7 @@ typedef enum eOpacityGpencil_Flag {
GP_OPACITY_INVERT_MATERIAL = (1 << 5),
GP_OPACITY_CUSTOM_CURVE = (1 << 6),
GP_OPACITY_NORMALIZE = (1 << 7),
+ GP_OPACITY_FADING = (1 << 8),
} eOpacityGpencil_Flag;
typedef struct ArrayGpencilModifierData {
@@ -470,6 +485,39 @@ typedef enum eLatticeGpencil_Flag {
GP_LATTICE_INVERT_MATERIAL = (1 << 4),
} eLatticeGpencil_Flag;
+typedef struct LengthGpencilModifierData {
+ GpencilModifierData modifier;
+ /** Material for filtering. */
+ struct Material *material;
+ /** Layer name. */
+ char layername[64];
+ /** Custom index for passes. */
+ int pass_index;
+ /** Flags. */
+ int flag;
+ /** Custom index for passes. */
+ int layer_pass;
+ /** Length. */
+ float start_fac, end_fac;
+ /** Overshoot trajectory factor. */
+ float overshoot_fac;
+ /** Modifier mode. */
+ int mode;
+ char _pad[4];
+} LengthGpencilModifierData;
+
+typedef enum eLengthGpencil_Flag {
+ GP_LENGTH_INVERT_LAYER = (1 << 0),
+ GP_LENGTH_INVERT_PASS = (1 << 1),
+ GP_LENGTH_INVERT_LAYERPASS = (1 << 2),
+ GP_LENGTH_INVERT_MATERIAL = (1 << 3),
+} eLengthGpencil_Flag;
+
+typedef enum eLengthGpencil_Type {
+ GP_LENGTH_RELATIVE = 0,
+ GP_LENGTH_ABSOLUTE = 1,
+} eLengthGpencil_Type;
+
typedef struct MirrorGpencilModifierData {
GpencilModifierData modifier;
struct Object *object;
@@ -616,6 +664,14 @@ typedef struct OffsetGpencilModifierData {
float loc[3];
float rot[3];
float scale[3];
+ /** Random Offset. */
+ float rnd_offset[3];
+ /** Random Rotation. */
+ float rnd_rot[3];
+ /** Random Scales. */
+ float rnd_scale[3];
+ /** (first element is the index) random values. */
+ int seed;
/** Custom index for passes. */
int layer_pass;
} OffsetGpencilModifierData;
@@ -626,6 +682,7 @@ typedef enum eOffsetGpencil_Flag {
GP_OFFSET_INVERT_VGROUP = (1 << 2),
GP_OFFSET_INVERT_LAYERPASS = (1 << 3),
GP_OFFSET_INVERT_MATERIAL = (1 << 4),
+ GP_OFFSET_UNIFORM_RANDOM_SCALE = (1 << 5),
} eOffsetGpencil_Flag;
typedef struct SmoothGpencilModifierData {
@@ -819,7 +876,7 @@ typedef enum eLineartGpencilModifierSource {
typedef enum eLineArtGPencilModifierFlags {
LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 0),
LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 1),
- LRT_GPENCIL_SOFT_SELECTION = (1 << 2),
+ LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */,
LRT_GPENCIL_IS_BAKED = (1 << 3),
} eLineArtGPencilModifierFlags;
@@ -868,7 +925,7 @@ typedef struct LineartGpencilModifierData {
/* CPU mode */
float chaining_image_threshold;
- float resample_length;
+ int _pad;
/* Ported from SceneLineArt flags. */
int calculation_flags;
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 8facdca2f9c..ea3c1ff7275 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -47,7 +47,7 @@ struct Curve;
#define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2
#define GPENCIL_MIN_FILL_FAC 0.05f
-#define GPENCIL_MAX_FILL_FAC 5.0f
+#define GPENCIL_MAX_FILL_FAC 8.0f
/* ***************************************** */
/* GP Stroke Points */
@@ -112,6 +112,8 @@ typedef enum eGPDspoint_Flag {
GP_SPOINT_TAG = (1 << 1),
/* stroke point is temp tagged (for some editing operation) */
GP_SPOINT_TEMP_TAG = (1 << 2),
+ /* stroke point is temp tagged (for some editing operation) */
+ GP_SPOINT_TEMP_TAG2 = (1 << 3),
} eGPSPoint_Flag;
/* ***************************************** */
@@ -558,6 +560,8 @@ typedef enum eGPDlayer_Flag {
GP_LAYER_USE_MASK = (1 << 13), /*TODO: DEPRECATED */
/* Ruler Layer */
GP_LAYER_IS_RULER = (1 << 14),
+ /* Disable masks in viewlayer render */
+ GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER = (1 << 15),
} eGPDlayer_Flag;
/** #bGPDlayer.onion_flag */
diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h
index 8bb94976414..c5e207c4e20 100644
--- a/source/blender/makesdna/DNA_ipo_types.h
+++ b/source/blender/makesdna/DNA_ipo_types.h
@@ -77,8 +77,9 @@ typedef struct IpoCurve {
short totvert;
/** Interpolation and extrapolation modes . */
short ipo, extrap;
- /** Flag= settings; rt= ???. */
- short flag, rt;
+ /** Flag= settings. */
+ short flag;
+ char _pad0[2];
/** Minimum/maximum y-extents for curve. */
float ymin, ymax;
/** ???. */
diff --git a/source/blender/makesdna/DNA_light_defaults.h b/source/blender/makesdna/DNA_light_defaults.h
index f3ccf14ac5b..5e5ce4bf540 100644
--- a/source/blender/makesdna/DNA_light_defaults.h
+++ b/source/blender/makesdna/DNA_light_defaults.h
@@ -68,6 +68,7 @@
.volume_fac = 1.0f, \
.att_dist = 40.0f, \
.sun_angle = DEG2RADF(0.526f), \
+ .area_spread = DEG2RADF(180.0f), \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h
index 3b7440aedd2..82ff3c95834 100644
--- a/source/blender/makesdna/DNA_light_types.h
+++ b/source/blender/makesdna/DNA_light_types.h
@@ -70,9 +70,9 @@ typedef struct Light {
short area_shape;
float area_size, area_sizey, area_sizez;
+ float area_spread;
float sun_angle;
- char _pad3[4];
/* texact is for buttons */
short texact, shadhalostep;
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
index 31e221b74a0..860a6579bf7 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -35,9 +35,6 @@
#include "DNA_ID.h"
#include "DNA_listBase.h"
-struct Object;
-struct Material;
-
/* Notice that we need to have this file although no struct defines.
* Edge flags and usage flags are used by with scene/object/gpencil modifier bits, and those values
* needs to stay consistent throughout. */
diff --git a/source/blender/makesdna/DNA_mesh_defaults.h b/source/blender/makesdna/DNA_mesh_defaults.h
index 8326db66049..889f92eec95 100644
--- a/source/blender/makesdna/DNA_mesh_defaults.h
+++ b/source/blender/makesdna/DNA_mesh_defaults.h
@@ -37,6 +37,7 @@
.face_sets_color_seed = 0, \
.face_sets_color_default = 1, \
.flag = ME_REMESH_FIX_POLES | ME_REMESH_REPROJECT_VOLUME, \
+ .editflag = ME_EDIT_MIRROR_VERTEX_GROUPS \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index af20a63b907..3eb5920dfe6 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -263,7 +263,7 @@ enum {
/* me->editflag */
enum {
- ME_EDIT_VERTEX_GROUPS_X_SYMMETRY = 1 << 0,
+ ME_EDIT_MIRROR_VERTEX_GROUPS = 1 << 0,
ME_EDIT_MIRROR_Y = 1 << 1, /* unused so far */
ME_EDIT_MIRROR_Z = 1 << 2, /* unused so far */
@@ -272,7 +272,11 @@ enum {
ME_EDIT_PAINT_VERT_SEL = 1 << 5,
};
-/* we cant have both flags enabled at once,
+/* Helper macro to see if vertex group X mirror is on. */
+#define ME_USING_MIRROR_X_VERTEX_GROUPS(_me) \
+ (((_me)->editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) && ((_me)->symmetry & ME_SYMMETRY_X))
+
+/* We cant have both flags enabled at once,
* flags defined in DNA_scene_types.h */
#define ME_EDIT_PAINT_SEL_MODE(_me) \
(((_me)->editflag & ME_EDIT_PAINT_FACE_SEL) ? \
diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h
index 9335c360363..f6dac88051b 100644
--- a/source/blender/makesdna/DNA_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_modifier_defaults.h
@@ -414,7 +414,8 @@
{ \
.cache_file = NULL, \
.object_path = "", \
- .read_flag = MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR, \
+ .read_flag = MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | \
+ MOD_MESHSEQ_READ_COLOR | MOD_MESHSEQ_INTERPOLATE_VERTICES, \
.velocity_scale = 1.0f, \
.reader = NULL, \
.reader_object_path = "", \
@@ -428,6 +429,7 @@
{ \
.flag = MOD_MIR_AXIS_X | MOD_MIR_VGROUP, \
.tolerance = 0.001f, \
+ .bisect_threshold = 0.001f, \
.uv_offset = {0.0f, 0.0f}, \
.uv_offset_copy = {0.0f, 0.0f}, \
.mirror_ob = NULL, \
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index ca6f1467d9c..c61e940190f 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -368,6 +368,8 @@ typedef struct MirrorModifierData {
short axis DNA_DEPRECATED;
short flag;
float tolerance;
+ float bisect_threshold;
+ char _pad[4];
float uv_offset[2];
float uv_offset_copy[2];
struct Object *mirror_ob;
diff --git a/source/blender/makesdna/DNA_movieclip_defaults.h b/source/blender/makesdna/DNA_movieclip_defaults.h
index 4aa1bd779c2..753147eb072 100644
--- a/source/blender/makesdna/DNA_movieclip_defaults.h
+++ b/source/blender/makesdna/DNA_movieclip_defaults.h
@@ -32,7 +32,7 @@
.build_size_flag = IMB_PROXY_25, \
.build_tc_flag = IMB_TC_RECORD_RUN | IMB_TC_FREE_RUN | \
IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN | IMB_TC_RECORD_RUN_NO_GAPS, \
- .quality = 90, \
+ .quality = 50, \
}
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index f24d0e40d19..2ae48bfe0ad 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -45,6 +45,8 @@ struct bNodePreview;
struct bNodeTreeExec;
struct bNodeType;
struct uiBlock;
+struct Tex;
+struct Material;
#define NODE_MAXSTR 64
@@ -98,8 +100,8 @@ typedef struct bNodeSocket {
void *storage;
short type, flag;
- /** Max. number of links. Read via nodeSocketLinkLimit, because the limit might be defined on the
- * socket type. */
+ /** Max. number of links. Read via nodeSocketLinkLimit,
+ * because the limit might be defined on the socket type. */
short limit;
/** Input/output type. */
short in_out;
@@ -125,6 +127,7 @@ typedef struct bNodeSocket {
/** Custom dynamic defined label, MAX_NAME. */
char label[64];
+ char description[64];
/** Cached data from execution. */
void *cache;
@@ -164,9 +167,11 @@ typedef enum eNodeSocketDatatype {
SOCK_IMAGE = 9,
SOCK_GEOMETRY = 10,
SOCK_COLLECTION = 11,
+ SOCK_TEXTURE = 12,
+ SOCK_MATERIAL = 13,
} eNodeSocketDatatype;
-/* socket shape */
+/* Socket shape. */
typedef enum eNodeSocketDisplayShape {
SOCK_DISPLAY_SHAPE_CIRCLE = 0,
SOCK_DISPLAY_SHAPE_SQUARE = 1,
@@ -176,38 +181,43 @@ typedef enum eNodeSocketDisplayShape {
SOCK_DISPLAY_SHAPE_DIAMOND_DOT = 5,
} eNodeSocketDisplayShape;
-/* socket side (input/output) */
+/* Socket side (input/output). */
typedef enum eNodeSocketInOut {
SOCK_IN = 1 << 0,
SOCK_OUT = 1 << 1,
} eNodeSocketInOut;
-/* sock->flag, first bit is select */
+/* #bNodeSocket.flag, first bit is selection. */
typedef enum eNodeSocketFlag {
- /** hidden is user defined, to hide unused */
+ /** Hidden is user defined, to hide unused sockets. */
SOCK_HIDDEN = (1 << 1),
- /** for quick check if socket is linked */
+ /** For quick check if socket is linked. */
SOCK_IN_USE = (1 << 2),
- /** unavailable is for dynamic sockets */
+ /** Unavailable is for dynamic sockets. */
SOCK_UNAVAIL = (1 << 3),
// /** DEPRECATED dynamic socket (can be modified by user) */
// SOCK_DYNAMIC = (1 << 4),
// /** DEPRECATED group socket should not be exposed */
// SOCK_INTERNAL = (1 << 5),
- /** socket collapsed in UI */
+ /** Socket collapsed in UI. */
SOCK_COLLAPSED = (1 << 6),
- /** hide socket value, if it gets auto default */
+ /** Hide socket value, if it gets auto default. */
SOCK_HIDE_VALUE = (1 << 7),
- /** socket hidden automatically, to distinguish from manually hidden */
+ /** Socket hidden automatically, to distinguish from manually hidden. */
SOCK_AUTO_HIDDEN__DEPRECATED = (1 << 8),
SOCK_NO_INTERNAL_LINK = (1 << 9),
/** Draw socket in a more compact form. */
SOCK_COMPACT = (1 << 10),
/** Make the input socket accept multiple incoming links in the UI. */
SOCK_MULTI_INPUT = (1 << 11),
+ /**
+ * Don't show the socket's label in the interface, for situations where the
+ * type is obvious and the name takes up too much space.
+ */
+ SOCK_HIDE_LABEL = (1 << 12),
} eNodeSocketFlag;
-/* limit data in bNode to what we want to see saved? */
+/* TODO: Limit data in bNode to what we want to see saved. */
typedef struct bNode {
struct bNode *next, *prev, *new_node;
@@ -348,6 +358,8 @@ typedef struct bNode {
* composite out nodes when editing tree
*/
#define NODE_DO_OUTPUT_RECALC (1 << 17)
+/* A preview for the data in this node can be displayed in the spreadsheet editor. */
+#define NODE_ACTIVE_PREVIEW (1 << 18)
/* node->update */
/* XXX NODE_UPDATE is a generic update flag. More fine-grained updates
@@ -454,7 +466,6 @@ typedef struct bNodeTree {
short is_updating;
/** Generic temporary flag for recursion check (DFS/BFS). */
short done;
- char _pad2[4];
/** Specific node type this tree is used for. */
int nodetype DNA_DEPRECATED;
@@ -465,6 +476,8 @@ typedef struct bNodeTree {
short render_quality;
/** Tile size for compositor engine. */
int chunksize;
+ /** Execution mode to use for compositor engine. */
+ int execution_mode;
rctf viewer_border;
@@ -495,7 +508,7 @@ typedef struct bNodeTree {
*/
struct bNodeTreeExec *execdata;
- /* callbacks */
+ /* Callbacks. */
void (*progress)(void *, float progress);
/** \warning may be called by different threads */
void (*stats_draw)(void *, const char *str);
@@ -538,6 +551,12 @@ typedef enum eNodeTreeUpdate {
NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT),
} eNodeTreeUpdate;
+/* tree->execution_mode */
+typedef enum eNodeTreeExecutionMode {
+ NTREE_EXECUTION_MODE_TILED = 0,
+ NTREE_EXECUTION_MODE_FULL_FRAME = 1,
+} eNodeTreeExecutionMode;
+
/* socket value structs for input buttons
* DEPRECATED now using ID properties
*/
@@ -590,7 +609,15 @@ typedef struct bNodeSocketValueCollection {
struct Collection *value;
} bNodeSocketValueCollection;
-/* data structs, for node->storage */
+typedef struct bNodeSocketValueTexture {
+ struct Tex *value;
+} bNodeSocketValueTexture;
+
+typedef struct bNodeSocketValueMaterial {
+ struct Material *value;
+} bNodeSocketValueMaterial;
+
+/* Data structs, for node->storage. */
enum {
CMP_NODE_MASKTYPE_ADD = 0,
CMP_NODE_MASKTYPE_SUBTRACT = 1,
@@ -614,7 +641,7 @@ enum {
CMP_NODEFLAG_MASK_NO_FEATHER = (1 << 1),
CMP_NODEFLAG_MASK_MOTION_BLUR = (1 << 2),
- /* we may want multiple aspect options, exposed as an rna enum */
+ /* We may want multiple aspect options, exposed as an rna enum. */
CMP_NODEFLAG_MASK_FIXED = (1 << 8),
CMP_NODEFLAG_MASK_FIXED_SCENE = (1 << 9),
};
@@ -629,7 +656,7 @@ typedef struct NodeFrame {
short label_size;
} NodeFrame;
-/* this one has been replaced with ImageUser, keep it for do_versions() */
+/* This one has been replaced with ImageUser, keep it for do_versions(). */
typedef struct NodeImageAnim {
int frames DNA_DEPRECATED;
int sfra DNA_DEPRECATED;
@@ -683,7 +710,7 @@ typedef struct NodeEllipseMask {
char _pad[4];
} NodeEllipseMask;
-/* layer info for image node outputs */
+/* Layer info for image node outputs. */
typedef struct NodeImageLayer {
/* index in the Image->layers->passes lists */
int pass_index DNA_DEPRECATED;
@@ -715,6 +742,12 @@ typedef struct NodeBilateralBlurData {
char _pad[2];
} NodeBilateralBlurData;
+typedef struct NodeAntiAliasingData {
+ float threshold;
+ float contrast_limit;
+ float corner_rounding;
+} NodeAntiAliasingData;
+
/* NOTE: Only for do-version code. */
typedef struct NodeHueSat {
float hue, sat, val;
@@ -1108,6 +1141,14 @@ typedef struct NodeDenoise {
char hdr;
} NodeDenoise;
+typedef struct NodeAttributeClamp {
+ /* CustomDataType. */
+ uint8_t data_type;
+
+ /* NodeClampOperation. */
+ uint8_t operation;
+} NodeAttributeClamp;
+
typedef struct NodeAttributeCompare {
/* FloatCompareOperation. */
uint8_t operation;
@@ -1119,6 +1160,14 @@ typedef struct NodeAttributeCompare {
char _pad[5];
} NodeAttributeCompare;
+typedef struct NodeAttributeMapRange {
+ /* GeometryNodeAttributeDataType */
+ uint8_t data_type;
+
+ /* NodeMapRangeType. */
+ uint8_t interpolation_type;
+} NodeAttributeMapRange;
+
typedef struct NodeAttributeMath {
/* NodeMathOperation. */
uint8_t operation;
@@ -1159,10 +1208,31 @@ typedef struct NodeAttributeVectorMath {
uint8_t input_type_c;
} NodeAttributeVectorMath;
+typedef struct NodeAttributeVectorRotate {
+ /* GeometryNodeAttributeVectorRotateMode */
+ uint8_t mode;
+
+ /* GeometryNodeAttributeInputMode */
+ uint8_t input_type_vector;
+ uint8_t input_type_center;
+ uint8_t input_type_axis;
+ uint8_t input_type_angle;
+ uint8_t input_type_rotation;
+ char _pad[2];
+} NodeAttributeVectorRotate;
+
typedef struct NodeAttributeColorRamp {
ColorBand color_ramp;
} NodeAttributeColorRamp;
+typedef struct NodeAttributeCurveMap {
+ /* CustomDataType. */
+ uint8_t data_type;
+ char _pad[7];
+ CurveMapping *curve_vec;
+ CurveMapping *curve_rgb;
+} NodeAttributeCurveMap;
+
typedef struct NodeInputVector {
float vector[3];
} NodeInputVector;
@@ -1255,10 +1325,9 @@ typedef struct NodeAttributeSeparateXYZ {
typedef struct NodeAttributeConvert {
/* CustomDataType. */
- uint8_t data_type;
- char _pad[1];
+ int8_t data_type;
/* AttributeDomain. */
- int16_t domain;
+ int8_t domain;
} NodeAttributeConvert;
typedef struct NodeGeometryMeshCircle {
@@ -1283,6 +1352,23 @@ typedef struct NodeGeometryMeshLine {
uint8_t count_mode;
} NodeGeometryMeshLine;
+typedef struct NodeSwitch {
+ /* NodeSwitch. */
+ uint8_t input_type;
+} NodeSwitch;
+
+typedef struct NodeGeometryCurveResample {
+ /* GeometryNodeCurveSampleMode. */
+ uint8_t mode;
+} NodeGeometryCurveResample;
+
+typedef struct NodeGeometryAttributeTransfer {
+ /* AttributeDomain. */
+ int8_t domain;
+ /* GeometryNodeAttributeTransferMapMode. */
+ uint8_t mapping;
+} NodeGeometryAttributeTransfer;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1457,7 +1543,7 @@ enum {
#define SHD_AO_INSIDE 1
#define SHD_AO_LOCAL 2
-/* Mapping node vector types */
+/* Mapping node vector types. */
enum {
NODE_MAPPING_TYPE_POINT = 0,
NODE_MAPPING_TYPE_TEXTURE = 1,
@@ -1465,7 +1551,7 @@ enum {
NODE_MAPPING_TYPE_NORMAL = 3,
};
-/* Rotation node vector types */
+/* Rotation node vector types. */
enum {
NODE_VECTOR_ROTATE_TYPE_AXIS = 0,
NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1,
@@ -1552,6 +1638,7 @@ typedef enum NodeVectorMathOperation {
NODE_VECTOR_MATH_TANGENT = 23,
NODE_VECTOR_MATH_REFRACT = 24,
NODE_VECTOR_MATH_FACEFORWARD = 25,
+ NODE_VECTOR_MATH_MULTIPLY_ADD = 26,
} NodeVectorMathOperation;
/* Boolean math node operations. */
@@ -1678,14 +1765,12 @@ typedef enum GeometryNodeAttributeProximityTargetType {
GEO_NODE_ATTRIBUTE_PROXIMITY_TARGET_GEOMETRY_ELEMENT_FACES = 2,
} GeometryNodeAttributeProximityTargetType;
-/* Boolean Node */
typedef enum GeometryNodeBooleanOperation {
GEO_NODE_BOOLEAN_INTERSECT = 0,
GEO_NODE_BOOLEAN_UNION = 1,
GEO_NODE_BOOLEAN_DIFFERENCE = 2,
} GeometryNodeBooleanOperation;
-/* Triangulate Node */
typedef enum GeometryNodeTriangulateNGons {
GEO_NODE_TRIANGULATE_NGON_BEAUTY = 0,
GEO_NODE_TRIANGULATE_NGON_EARCLIP = 1,
@@ -1726,6 +1811,14 @@ typedef enum GeometryNodeRotatePointsType {
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
} GeometryNodeRotatePointsType;
+typedef enum GeometryNodeAttributeVectorRotateMode {
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y = 2,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z = 3,
+ GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ = 4,
+} GeometryNodeAttributeVectorRotateMode;
+
typedef enum GeometryNodeAttributeRandomizeMode {
GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE = 0,
GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD = 1,
@@ -1777,6 +1870,16 @@ typedef enum GeometryNodeMeshLineCountMode {
GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1,
} GeometryNodeMeshLineCountMode;
+typedef enum GeometryNodeCurveSampleMode {
+ GEO_NODE_CURVE_SAMPLE_COUNT = 0,
+ GEO_NODE_CURVE_SAMPLE_LENGTH = 1,
+} GeometryNodeCurveSampleMode;
+
+typedef enum GeometryNodeAttributeTransferMapMode {
+ GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0,
+ GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1,
+} GeometryNodeAttributeTransferMapMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h
index 37013f5b4d6..3d8418fb734 100644
--- a/source/blender/makesdna/DNA_object_force_types.h
+++ b/source/blender/makesdna/DNA_object_force_types.h
@@ -182,8 +182,8 @@ typedef struct EffectorWeights {
/** Effector type specific weights. */
float weight[14];
float global_gravity;
- short flag, rt[3];
- char _pad[4];
+ short flag;
+ char _pad[2];
} EffectorWeights;
/* EffectorWeights->flag */
@@ -267,10 +267,9 @@ typedef struct SoftBody {
char namedVG_Spring_K[64];
/* baking */
- int sfra, efra;
- int interval;
+ char _pad1[6];
/** Local==1: use local coords for baking. */
- short local, solverflags;
+ char local, solverflags;
/* -- these must be kept for backwards compatibility -- */
/** Array of size totpointkey. */
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 686cf2048eb..9951bdefbbb 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -144,6 +144,9 @@ typedef struct Object_Runtime {
*/
char is_data_eval_owned;
+ /** Start time of the mode transfer overlay animation. */
+ double overlay_mode_transfer_start_time;
+
/** Axis aligned boundbox (in localspace). */
struct BoundBox *bb;
@@ -169,6 +172,12 @@ typedef struct Object_Runtime {
struct GeometrySet *geometry_set_eval;
/**
+ * A GHash that contains geometry sets for intermediate stages of evaluation. The keys are just a
+ * hash and are not owned by the map. The geometry sets are owned.
+ */
+ void *geometry_set_previews;
+
+ /**
* Mesh structure created during object evaluation.
* It has deformation only modifiers applied on it.
*/
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index cc40e26b92b..30b1fbe09d3 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -64,7 +64,7 @@ typedef struct BoidParticle {
struct BoidData data;
float gravity[3];
float wander[3];
- float rt;
+ char _pad0[4];
} BoidParticle;
typedef struct ParticleSpring {
@@ -82,7 +82,7 @@ typedef struct ChildParticle {
float w[4];
/** Face vertex weights and offset. */
float fuv[4], foffset;
- float rt;
+ char _pad0[4];
} ChildParticle;
typedef struct ParticleTarget {
@@ -99,7 +99,8 @@ typedef struct ParticleDupliWeight {
short count;
short flag;
/** Only updated on file save and used on file load. */
- short index, rt;
+ short index;
+ char _pad0[2];
} ParticleDupliWeight;
typedef struct ParticleData {
@@ -191,7 +192,8 @@ typedef struct ParticleSettings {
struct EffectorWeights *effector_weights;
struct Collection *collision_group;
- int flag, rt;
+ int flag;
+ char _pad1[4];
short type, from, distr, texact;
/* physics modes */
short phystype, rotmode, avemode, reactevent;
diff --git a/source/blender/makesdna/DNA_pointcache_types.h b/source/blender/makesdna/DNA_pointcache_types.h
index de2fa3f10fe..ad5f386bf2b 100644
--- a/source/blender/makesdna/DNA_pointcache_types.h
+++ b/source/blender/makesdna/DNA_pointcache_types.h
@@ -107,7 +107,8 @@ typedef struct PointCache {
int totpoint;
/** Modifier stack index. */
int index;
- short compression, rt;
+ short compression;
+ char _pad0[2];
char name[64];
char prev_name[64];
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c7f7e610a1a..0b07b8271a5 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -952,7 +952,8 @@ typedef struct ParticleEditSettings {
/** Runtime. */
void *paintcursor;
- float emitterdist, rt;
+ float emitterdist;
+ char _pad0[4];
int selectmode;
int edittype;
@@ -1550,7 +1551,8 @@ typedef struct UnitSettings {
typedef struct PhysicsSettings {
float gravity[3];
- int flag, quick_cache_step, rt;
+ int flag, quick_cache_step;
+ char _pad0[4];
} PhysicsSettings;
/* ------------------------------------------- */
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index fa11a7dfd13..7e0bf81457d 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -57,6 +57,7 @@ typedef struct StripElem {
char name[256];
/** Ignore when zeroed. */
int orig_width, orig_height;
+ float orig_fps;
} StripElem;
typedef struct StripCrop {
@@ -172,7 +173,7 @@ typedef struct Sequence {
float sat;
float mul, handsize;
- short anim_preseek;
+ short anim_preseek; /* UNUSED. */
/** Streamindex for movie or sound files with several streams. */
short streamindex;
/** For multicam source selection. */
@@ -517,11 +518,11 @@ enum {
SEQ_AUDIO_PAN_ANIMATED = (1 << 26),
SEQ_AUDIO_DRAW_WAVEFORM = (1 << 27),
- /* don't include Grease Pencil in OpenGL previews of Scene strips */
- SEQ_SCENE_NO_GPENCIL = (1 << 28),
+ /* don't include Annotations in OpenGL previews of Scene strips */
+ SEQ_SCENE_NO_ANNOTATION = (1 << 28),
SEQ_USE_VIEWS = (1 << 29),
- /* access scene strips directly (like a metastrip) */
+ /* Access scene strips directly (like a meta-strip). */
SEQ_SCENE_STRIPS = (1 << 30),
SEQ_INVALID_EFFECT = (1u << 31),
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index c170e711756..71764d9308c 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -323,6 +323,8 @@ typedef enum eSpaceOutliner_Filter {
SO_FILTER_NO_CHILDREN = (1 << 4),
SO_FILTER_UNUSED_5 = (1 << 5), /* cleared */
+ /** Show overrides that are defined/controlled by Blender. */
+ SO_FILTER_SHOW_SYSTEM_OVERRIDES = SO_FILTER_UNUSED_5, /* re-use */
SO_FILTER_NO_OB_MESH = (1 << 6),
SO_FILTER_NO_OB_ARMATURE = (1 << 7),
SO_FILTER_NO_OB_EMPTY = (1 << 8),
@@ -391,6 +393,7 @@ typedef enum eSpaceOutliner_Mode {
/* SO_KEYMAP = 13, */ /* deprecated! */
SO_ID_ORPHANS = 14,
SO_VIEW_LAYER = 15,
+ SO_OVERRIDES_LIBRARY = 16,
} eSpaceOutliner_Mode;
/* SpaceOutliner.storeflag */
@@ -777,8 +780,16 @@ typedef struct FileAssetSelectParams {
FileSelectParams base_params;
FileSelectAssetLibraryUID asset_library;
+
+ short import_type; /* eFileAssetImportType */
+ char _pad[6];
} FileAssetSelectParams;
+typedef enum eFileAssetImportType {
+ FILE_ASSET_IMPORT_LINK = 0,
+ FILE_ASSET_IMPORT_APPEND = 1,
+} eFileAssetImportType;
+
/**
* A wrapper to store previous and next folder lists (#FolderList) for a specific browse mode
* (#eFileBrowse_Mode).
@@ -1520,6 +1531,7 @@ typedef struct bNodeTreePath {
/** MAX_NAME. */
char node_name[64];
+ char display_name[64];
} bNodeTreePath;
typedef struct SpaceNode {
@@ -1850,6 +1862,46 @@ typedef struct SpaceStatusBar {
/** \name Spreadsheet
* \{ */
+typedef struct SpreadsheetColumnID {
+ char *name;
+} SpreadsheetColumnID;
+
+typedef struct SpreadsheetColumn {
+ struct SpreadsheetColumn *next, *prev;
+ /**
+ * Identifies the data in the column.
+ * This is a pointer instead of a struct to make it easier if we want to "subclass"
+ * #SpreadsheetColumnID in the future for different kinds of ids.
+ */
+ SpreadsheetColumnID *id;
+} SpreadsheetColumn;
+
+/**
+ * An item in SpaceSpreadsheet.context_path.
+ * This is a bases struct for the structs below.
+ */
+typedef struct SpreadsheetContext {
+ struct SpreadsheetContext *next, *prev;
+ /* eSpaceSpreadsheet_ContextType. */
+ int type;
+ char _pad[4];
+} SpreadsheetContext;
+
+typedef struct SpreadsheetContextObject {
+ SpreadsheetContext base;
+ struct Object *object;
+} SpreadsheetContextObject;
+
+typedef struct SpreadsheetContextModifier {
+ SpreadsheetContext base;
+ char *modifier_name;
+} SpreadsheetContextModifier;
+
+typedef struct SpreadsheetContextNode {
+ SpreadsheetContext base;
+ char *node_name;
+} SpreadsheetContextNode;
+
typedef struct SpaceSpreadsheet {
SpaceLink *next, *prev;
/** Storage of regions for inactive spaces. */
@@ -1859,7 +1911,16 @@ typedef struct SpaceSpreadsheet {
char _pad0[6];
/* End 'SpaceLink' header. */
- struct ID *pinned_id;
+ /* List of #SpreadsheetColumn. */
+ ListBase columns;
+
+ /**
+ * List of #SpreadsheetContext.
+ * This is a path to the data that is displayed in the spreadsheet.
+ * It can be set explicitly by an action of the user (e.g. clicking the preview icon in a
+ * geometry node) or it can be derived from context automatically based on some heuristic.
+ */
+ ListBase context_path;
/* eSpaceSpreadsheet_FilterFlag. */
uint8_t filter_flag;
@@ -1871,22 +1932,41 @@ typedef struct SpaceSpreadsheet {
/* eSpaceSpreadsheet_ObjectContext. */
uint8_t object_eval_state;
- char _pad1[4];
+ /* eSpaceSpreadsheet_Flag. */
+ uint32_t flag;
SpaceSpreadsheet_Runtime *runtime;
} SpaceSpreadsheet;
-/** \} */
+typedef enum eSpaceSpreadsheet_Flag {
+ SPREADSHEET_FLAG_PINNED = (1 << 0),
+ SPREADSHEET_FLAG_CONTEXT_PATH_COLLAPSED = (1 << 1),
+} eSpaceSpreadsheet_Flag;
typedef enum eSpaceSpreadsheet_FilterFlag {
SPREADSHEET_FILTER_SELECTED_ONLY = (1 << 0),
} eSpaceSpreadsheet_FilterFlag;
typedef enum eSpaceSpreadsheet_ObjectEvalState {
- SPREADSHEET_OBJECT_EVAL_STATE_FINAL = 0,
+ SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED = 0,
SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL = 1,
} eSpaceSpreadsheet_Context;
+typedef enum eSpaceSpreadsheet_ContextType {
+ SPREADSHEET_CONTEXT_OBJECT = 0,
+ SPREADSHEET_CONTEXT_MODIFIER = 1,
+ SPREADSHEET_CONTEXT_NODE = 2,
+} eSpaceSpreadsheet_ContextType;
+
+/**
+ * We can't just use UI_UNIT_X, because it does not take `widget.points` into account, which
+ * modifies the width of text as well.
+ */
+#define SPREADSHEET_WIDTH_UNIT \
+ (UI_UNIT_X * UI_style_get_dpi()->widget.points / (float)UI_DEFAULT_TEXT_POINTS)
+
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Space Defines (eSpace_Type)
* \{ */
diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h
index 5381c524e4d..60c255e8637 100644
--- a/source/blender/makesdna/DNA_texture_types.h
+++ b/source/blender/makesdna/DNA_texture_types.h
@@ -57,7 +57,8 @@ typedef struct MTex {
short colormodel, pmapto, pmaptoneg;
short normapspace, which_output;
float r, g, b, k;
- float def_var, rt;
+ float def_var;
+ char _pad1[4];
/* common */
float colfac, varfac;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 4595b12e9d4..61d2c04d98b 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -642,11 +642,12 @@ typedef struct UserDef_Experimental {
* when the release cycle is not alpha. */
char use_new_hair_type;
char use_new_point_cloud_type;
+ char use_full_frame_compositor;
char use_sculpt_vertex_colors;
- char use_switch_object_operator;
char use_sculpt_tools_tilt;
char use_asset_browser;
- char _pad[6];
+ char use_override_templates;
+ char _pad[5];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h
index 10d0bafec61..9dfc37e57b1 100644
--- a/source/blender/makesdna/DNA_view3d_defaults.h
+++ b/source/blender/makesdna/DNA_view3d_defaults.h
@@ -70,6 +70,7 @@
\
.gpencil_paper_opacity = 0.5f, \
.gpencil_grid_opacity = 0.9f, \
+ .gpencil_vertex_paint_opacity = 1.0f, \
}
#define _DNA_DEFAULT_View3DCursor \
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index b8e2256c3c6..2f4e4e57b9f 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -367,7 +367,7 @@ typedef struct View3D {
#define V3D_LOCAL_COLLECTIONS (1 << 0)
#define V3D_FLAG_UNUSED_1 (1 << 1) /* cleared */
#define V3D_HIDE_HELPLINES (1 << 2)
-#define V3D_INVALID_BACKBUF (1 << 3)
+#define V3D_FLAG_UNUSED_2 (1 << 3) /* cleared */
#define V3D_XR_SESSION_MIRROR (1 << 4)
#define V3D_FLAG_UNUSED_10 (1 << 10) /* cleared */
@@ -380,6 +380,8 @@ typedef struct View3D {
enum {
/** The 3D view which the XR session was created in is flagged with this. */
V3D_RUNTIME_XR_SESSION_ROOT = (1 << 0),
+ /** Some operators override the depth buffer for dedicated occlusion operations. */
+ V3D_RUNTIME_DEPTHBUF_OVERRIDDEN = (1 << 1),
};
/** #RegionView3D.persp */
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 68d69a671ba..59091fec4b8 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -278,8 +278,14 @@ typedef struct wmWindow {
char event_queue_check_click;
/** Enable when #KM_PRESS events are not handled (keyboard/mouse-buttons only). */
char event_queue_check_drag;
+ /**
+ * Enable when the drag was handled,
+ * to avoid mouse-motion continually triggering drag events which are not handled
+ * but add overhead to gizmo handling (for example), see T87511.
+ */
+ char event_queue_check_drag_handled;
- char _pad0[2];
+ char _pad0[1];
/** Internal, lock pie creation from this event until released. */
short pie_event_type_lock;
@@ -374,7 +380,12 @@ typedef struct wmKeyMapItem {
/** Unique identifier. Positive for kmi that override builtins, negative otherwise. */
short id;
char _pad[2];
- /** Rna pointer to access properties. */
+ /**
+ * RNA pointer to access properties.
+ *
+ * \note The `ptr.owner_id` value must be NULL, as a signal not to use the context
+ * when running property callbacks such as ENUM item functions.
+ */
struct PointerRNA *ptr;
} wmKeyMapItem;
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
index 2ce32a723a7..8e63760fef7 100644
--- a/source/blender/makesdna/DNA_xr_types.h
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -58,6 +58,21 @@ typedef enum eXRSessionBasePoseType {
XR_BASE_POSE_CUSTOM = 2,
} eXRSessionBasePoseType;
+/** XR action type. Enum values match those in GHOST_XrActionType enum for consistency. */
+typedef enum eXrActionType {
+ XR_BOOLEAN_INPUT = 1,
+ XR_FLOAT_INPUT = 2,
+ XR_VECTOR2F_INPUT = 3,
+ XR_POSE_INPUT = 4,
+ XR_VIBRATION_OUTPUT = 100,
+} eXrActionType;
+
+typedef enum eXrOpFlag {
+ XR_OP_PRESS = 0,
+ XR_OP_RELEASE = 1,
+ XR_OP_MODAL = 2,
+} eXrOpFlag;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 95272fb7804..2d55ea05867 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -316,6 +316,7 @@ SDNA_DEFAULT_DECL_STRUCT(ThickGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData);
+SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@@ -541,6 +542,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(TimeGpencilModifierData),
SDNA_DEFAULT_DECL(TintGpencilModifierData),
SDNA_DEFAULT_DECL(LineartGpencilModifierData),
+ SDNA_DEFAULT_DECL(LengthGpencilModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index 3690a1126d4..a23c9087ffc 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -792,6 +792,9 @@ static void cast_primitive_type(const eSDNA_Type old_type,
old_value_i = *((uint64_t *)old_data);
old_value_f = (double)old_value_i;
break;
+ case SDNA_TYPE_INT8:
+ old_value_i = (uint64_t) * ((int8_t *)old_data);
+ old_value_f = (double)old_value_i;
}
switch (new_type) {
@@ -828,6 +831,9 @@ static void cast_primitive_type(const eSDNA_Type old_type,
case SDNA_TYPE_UINT64:
*((uint64_t *)new_data) = old_value_i;
break;
+ case SDNA_TYPE_INT8:
+ *((int8_t *)new_data) = (int8_t)old_value_i;
+ break;
}
old_data += oldlen;
@@ -1194,7 +1200,10 @@ static void reconstruct_struct(const DNA_ReconstructInfo *reconstruct_info,
new_block + step->data.substruct.new_offset);
break;
case RECONSTRUCT_STEP_INIT_ZERO:
- /* Do nothing, because the memory block has been calloced. */
+ /* Do nothing, because the memory block are zeroed (from #MEM_callocN).
+ *
+ * Note that the struct could be initialized with the default struct,
+ * however this complicates versioning, especially with flags, see: D4500. */
break;
}
}
@@ -1555,9 +1564,8 @@ DNA_ReconstructInfo *DNA_reconstruct_info_create(const SDNA *oldsdna,
ReconstructStep *steps = create_reconstruct_steps_for_struct(
oldsdna, newsdna, compare_flags, old_struct, new_struct);
- int steps_len = new_struct->members_len;
/* Comment the line below to skip the compression for debugging purposes. */
- steps_len = compress_reconstruct_steps(steps, new_struct->members_len);
+ const int steps_len = compress_reconstruct_steps(steps, new_struct->members_len);
reconstruct_info->steps[new_struct_nr] = steps;
reconstruct_info->step_counts[new_struct_nr] = steps_len;
@@ -1652,6 +1660,7 @@ int DNA_elem_type_size(const eSDNA_Type elem_nr)
switch (elem_nr) {
case SDNA_TYPE_CHAR:
case SDNA_TYPE_UCHAR:
+ case SDNA_TYPE_INT8:
return 1;
case SDNA_TYPE_SHORT:
case SDNA_TYPE_USHORT:
diff --git a/source/blender/makesdna/intern/dna_utils.c b/source/blender/makesdna/intern/dna_utils.c
index 3cf5c52a4c6..f050934b5b3 100644
--- a/source/blender/makesdna/intern/dna_utils.c
+++ b/source/blender/makesdna/intern/dna_utils.c
@@ -235,9 +235,6 @@ void DNA_alias_maps(enum eDNA_RenameDir version_dir, GHash **r_struct_map, GHash
if (version_dir == DNA_RENAME_STATIC_FROM_ALIAS) {
const char *renames[][2] = {
- /* Disable 'int8_t' until we support 'signed char', since changing negative
- * values to a different type isn't supported and will change the value. */
- /* {"int8_t", "char"}, */
{"uint8_t", "uchar"},
{"int16_t", "short"},
{"uint16_t", "ushort"},
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 26fc56cfa1d..85bcc94c335 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -1222,6 +1222,7 @@ static int make_structDNA(const char *base_directory,
add_type("int64_t", 8); /* SDNA_TYPE_INT64 */
add_type("uint64_t", 8); /* SDNA_TYPE_UINT64 */
add_type("void", 0); /* SDNA_TYPE_VOID */
+ add_type("int8_t", 1); /* SDNA_TYPE_INT8 */
/* the defines above shouldn't be output in the padding file... */
const int firststruct = types_len;
@@ -1516,16 +1517,12 @@ int main(int argc, char **argv)
*
* - 'long': even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit,
* use int, int32_t or int64_t instead.
- * - 'int8_t': as DNA doesn't yet support 'signed char' types,
- * all char types are assumed to be unsigned.
- * We should be able to support this, it's just not something which has been added yet.
*
* Only valid use would be as a runtime variable if an API expected a long,
* but so far we don't have this happening.
*/
#ifdef __GNUC__
# pragma GCC poison long
-# pragma GCC poison int8_t
#endif
#include "DNA_ID.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index ba67cedfdbe..379e3e77b6e 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -336,6 +336,7 @@ extern StructRNA RNA_LatticeModifier;
extern StructRNA RNA_LatticePoint;
extern StructRNA RNA_LayerCollection;
extern StructRNA RNA_LayerObjects;
+extern StructRNA RNA_LengthGpencilModifier;
extern StructRNA RNA_Library;
extern StructRNA RNA_Light;
extern StructRNA RNA_LightProbe;
@@ -614,6 +615,10 @@ extern StructRNA RNA_Spline;
extern StructRNA RNA_SplineIKConstraint;
extern StructRNA RNA_SplinePoint;
extern StructRNA RNA_SpotLight;
+extern StructRNA RNA_SpreadsheetContext;
+extern StructRNA RNA_SpreadsheetContextObject;
+extern StructRNA RNA_SpreadsheetContextModifier;
+extern StructRNA RNA_SpreadsheetContextNode;
extern StructRNA RNA_Stereo3dDisplay;
extern StructRNA RNA_StretchToConstraint;
extern StructRNA RNA_StringAttribute;
@@ -841,6 +846,7 @@ const char *RNA_property_description(PropertyRNA *prop);
PropertyType RNA_property_type(PropertyRNA *prop);
PropertySubType RNA_property_subtype(PropertyRNA *prop);
PropertyUnit RNA_property_unit(PropertyRNA *prop);
+PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop);
int RNA_property_flag(PropertyRNA *prop);
int RNA_property_override_flag(PropertyRNA *prop);
int RNA_property_tags(PropertyRNA *prop);
diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h
index bc1a8f52b8d..a31182b2f5a 100644
--- a/source/blender/makesrna/RNA_define.h
+++ b/source/blender/makesrna/RNA_define.h
@@ -388,6 +388,7 @@ void RNA_def_property_string_default(PropertyRNA *prop, const char *value);
void RNA_def_property_ui_text(PropertyRNA *prop, const char *name, const char *description);
void RNA_def_property_ui_range(
PropertyRNA *prop, double min, double max, double step, int precision);
+void RNA_def_property_ui_scale_type(PropertyRNA *prop, PropertyScaleType scale_type);
void RNA_def_property_ui_icon(PropertyRNA *prop, int icon, int consecutive);
void RNA_def_property_update(PropertyRNA *prop, int noteflag, const char *updatefunc);
@@ -513,7 +514,7 @@ const char *RNA_property_typename(PropertyType type);
#define IS_DNATYPE_FLOAT_COMPAT(_str) (strcmp(_str, "float") == 0 || strcmp(_str, "double") == 0)
#define IS_DNATYPE_INT_COMPAT(_str) \
(strcmp(_str, "int") == 0 || strcmp(_str, "short") == 0 || strcmp(_str, "char") == 0 || \
- strcmp(_str, "uchar") == 0 || strcmp(_str, "ushort") == 0)
+ strcmp(_str, "uchar") == 0 || strcmp(_str, "ushort") == 0 || strcmp(_str, "int8_t") == 0)
#define IS_DNATYPE_BOOLEAN_COMPAT(_str) \
(IS_DNATYPE_INT_COMPAT(_str) || strcmp(_str, "int64_t") == 0 || strcmp(_str, "uint64_t") == 0)
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index b0895609e9a..71af69f77a1 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -237,6 +237,7 @@ extern const EnumPropertyItem rna_enum_curveprofile_preset_items[];
extern const EnumPropertyItem rna_enum_preference_section_items[];
extern const EnumPropertyItem rna_enum_attribute_type_items[];
+extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[];
extern const EnumPropertyItem rna_enum_attribute_domain_items[];
extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[];
extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free);
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index c4f6707dad5..8e1cfafcc42 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -82,19 +82,46 @@ typedef enum PropertyType {
/* also update rna_property_subtype_unit when you change this */
typedef enum PropertyUnit {
PROP_UNIT_NONE = (0 << 16),
- PROP_UNIT_LENGTH = (1 << 16), /* m */
- PROP_UNIT_AREA = (2 << 16), /* m^2 */
- PROP_UNIT_VOLUME = (3 << 16), /* m^3 */
- PROP_UNIT_MASS = (4 << 16), /* kg */
- PROP_UNIT_ROTATION = (5 << 16), /* radians */
- PROP_UNIT_TIME = (6 << 16), /* frame */
- PROP_UNIT_VELOCITY = (7 << 16), /* m/s */
- PROP_UNIT_ACCELERATION = (8 << 16), /* m/(s^2) */
- PROP_UNIT_CAMERA = (9 << 16), /* mm */
- PROP_UNIT_POWER = (10 << 16), /* W */
- PROP_UNIT_TEMPERATURE = (11 << 16), /* C */
+ PROP_UNIT_LENGTH = (1 << 16), /* m */
+ PROP_UNIT_AREA = (2 << 16), /* m^2 */
+ PROP_UNIT_VOLUME = (3 << 16), /* m^3 */
+ PROP_UNIT_MASS = (4 << 16), /* kg */
+ PROP_UNIT_ROTATION = (5 << 16), /* radians */
+ PROP_UNIT_TIME = (6 << 16), /* frame */
+ PROP_UNIT_TIME_ABSOLUTE = (7 << 16), /* time in seconds (independent of scene) */
+ PROP_UNIT_VELOCITY = (8 << 16), /* m/s */
+ PROP_UNIT_ACCELERATION = (9 << 16), /* m/(s^2) */
+ PROP_UNIT_CAMERA = (10 << 16), /* mm */
+ PROP_UNIT_POWER = (11 << 16), /* W */
+ PROP_UNIT_TEMPERATURE = (12 << 16), /* C */
} PropertyUnit;
+/**
+ * Use values besides #PROP_SCALE_LINEAR
+ * so the movement of the mouse doesn't map linearly to the value of the slider.
+ *
+ * For some settings it's useful to space motion in a non-linear way, see T77868.
+ *
+ * NOTE: The scale types are available for all float sliders.
+ * For integer sliders they are only available if they use the visible value bar.
+ * Sliders with logarithmic scale and value bar must have a range > 0
+ * while logarithmic sliders without the value bar can have a range of >= 0.
+ */
+typedef enum PropertyScaleType {
+ /** Linear scale (default). */
+ PROP_SCALE_LINEAR = 0,
+ /**
+ * Logarithmic scale
+ * - Maximum range: `0 <= x < inf`
+ */
+ PROP_SCALE_LOG = 1,
+ /**
+ * Cubic scale.
+ * - Maximum range: `-inf < x < inf`
+ */
+ PROP_SCALE_CUBIC = 2,
+} PropertyScaleType;
+
#define RNA_SUBTYPE_UNIT(subtype) ((subtype)&0x00FF0000)
#define RNA_SUBTYPE_VALUE(subtype) ((subtype) & ~0x00FF0000)
#define RNA_SUBTYPE_UNIT_VALUE(subtype) ((subtype) >> 16)
@@ -131,6 +158,7 @@ typedef enum PropertySubType {
PROP_FACTOR = 15,
PROP_ANGLE = 16 | PROP_UNIT_ROTATION,
PROP_TIME = 17 | PROP_UNIT_TIME,
+ PROP_TIME_ABSOLUTE = 17 | PROP_UNIT_TIME_ABSOLUTE,
/** Distance in 3d space, don't use for pixel distance for eg. */
PROP_DISTANCE = 18 | PROP_UNIT_LENGTH,
PROP_DISTANCE_CAMERA = 19 | PROP_UNIT_CAMERA,
@@ -619,7 +647,7 @@ typedef enum StructFlag {
/** Indicates that this struct is an ID struct, and to use reference-counting. */
STRUCT_ID = (1 << 0),
STRUCT_ID_REFCOUNT = (1 << 1),
- /** defaults on, clear for user preferences and similar */
+ /** defaults on, indicates when changes in members of a StructRNA should trigger undo steps. */
STRUCT_UNDO = (1 << 2),
/* internal flags */
@@ -636,6 +664,12 @@ typedef enum StructFlag {
STRUCT_PUBLIC_NAMESPACE = (1 << 9),
/** All subtypes are added too. */
STRUCT_PUBLIC_NAMESPACE_INHERIT = (1 << 10),
+ /**
+ * When the #PointerRNA.owner_id is NULL, this signifies the property should be accessed
+ * without any context (the key-map UI and import/export for example).
+ * So accessing the property should not read from the current context to derive values/limits.
+ */
+ STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID = (1 << 11),
} StructFlag;
typedef int (*StructValidateFunc)(struct PointerRNA *ptr, void *data, int *have_function);
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 4fafa356879..95b7b7e5406 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -99,7 +99,7 @@ set(DEFSRC
)
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
list(APPEND DEFSRC
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index b20d9218316..7a9cfa79324 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -684,6 +684,29 @@ static char *rna_def_property_get_func(
}
}
}
+
+ /* Check log scale sliders for negative range. */
+ if (prop->type == PROP_FLOAT) {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
+ /* NOTE: UI_BTYPE_NUM_SLIDER can't have a softmin of zero. */
+ if ((fprop->ui_scale_type == PROP_SCALE_LOG) && (fprop->hardmin < 0 || fprop->softmin < 0)) {
+ CLOG_ERROR(
+ &LOG, "\"%s.%s\", range for log scale < 0.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ return NULL;
+ }
+ }
+ if (prop->type == PROP_INT) {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
+ /* Only UI_BTYPE_NUM_SLIDER is implemented and that one can't have a softmin of zero. */
+ if ((iprop->ui_scale_type == PROP_SCALE_LOG) &&
+ (iprop->hardmin <= 0 || iprop->softmin <= 0)) {
+ CLOG_ERROR(
+ &LOG, "\"%s.%s\", range for log scale <= 0.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ return NULL;
+ }
+ }
}
func = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "get");
@@ -3178,6 +3201,8 @@ static const char *rna_property_subtypename(PropertySubType type)
return "PROP_ANGLE";
case PROP_TIME:
return "PROP_TIME";
+ case PROP_TIME_ABSOLUTE:
+ return "PROP_TIME_ABSOLUTE";
case PROP_DISTANCE:
return "PROP_DISTANCE";
case PROP_DISTANCE_CAMERA:
@@ -3243,6 +3268,8 @@ static const char *rna_property_subtype_unit(PropertySubType type)
return "PROP_UNIT_ROTATION";
case PROP_UNIT_TIME:
return "PROP_UNIT_TIME";
+ case PROP_UNIT_TIME_ABSOLUTE:
+ return "PROP_UNIT_TIME_ABSOLUTE";
case PROP_UNIT_VELOCITY:
return "PROP_UNIT_VELOCITY";
case PROP_UNIT_ACCELERATION:
@@ -3935,6 +3962,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(iprop->getarray_ex),
rna_function_string(iprop->setarray_ex),
rna_function_string(iprop->range_ex));
+ rna_int_print(f, iprop->ui_scale_type);
+ fprintf(f, ", ");
rna_int_print(f, iprop->softmin);
fprintf(f, ", ");
rna_int_print(f, iprop->softmax);
@@ -3969,6 +3998,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(fprop->getarray_ex),
rna_function_string(fprop->setarray_ex),
rna_function_string(fprop->range_ex));
+ rna_float_print(f, fprop->ui_scale_type);
+ fprintf(f, ", ");
rna_float_print(f, fprop->softmin);
fprintf(f, ", ");
rna_float_print(f, fprop->softmax);
@@ -4341,7 +4372,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_screen.c", NULL, RNA_def_screen},
{"rna_sculpt_paint.c", NULL, RNA_def_sculpt_paint},
{"rna_sequencer.c", "rna_sequencer_api.c", RNA_def_sequencer},
-#ifdef WITH_GEOMETRY_NODES
+#ifdef WITH_SIMULATION_DATABLOCK
{"rna_simulation.c", NULL, RNA_def_simulation},
#endif
{"rna_space.c", "rna_space_api.c", RNA_def_space},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 6e2005b7314..43b65b087bf 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -85,6 +85,47 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem rna_enum_override_library_property_operation_items[] = {
+ {IDOVERRIDE_LIBRARY_OP_NOOP,
+ "NOOP",
+ 0,
+ "No-Op",
+ "Does nothing, prevents adding actual overrides (NOT USED)"},
+ {IDOVERRIDE_LIBRARY_OP_REPLACE,
+ "REPLACE",
+ 0,
+ "Replace",
+ "Replace value of reference by overriding one"},
+ {IDOVERRIDE_LIBRARY_OP_ADD,
+ "DIFF_ADD",
+ 0,
+ "Differential",
+ "Stores and apply difference between reference and local value (NOT USED)"},
+ {IDOVERRIDE_LIBRARY_OP_SUBTRACT,
+ "DIFF_SUB",
+ 0,
+ "Differential",
+ "Stores and apply difference between reference and local value (NOT USED)"},
+ {IDOVERRIDE_LIBRARY_OP_MULTIPLY,
+ "FACT_MULTIPLY",
+ 0,
+ "Factor",
+ "Stores and apply multiplication factor between reference and local value (NOT USED)"},
+ {IDOVERRIDE_LIBRARY_OP_INSERT_AFTER,
+ "INSERT_AFTER",
+ 0,
+ "Insert After",
+ "Insert a new item into collection after the one referenced in subitem_reference_name or "
+ "_index"},
+ {IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE,
+ "INSERT_BEFORE",
+ 0,
+ "Insert Before",
+ "Insert a new item into collection after the one referenced in subitem_reference_name or "
+ "_index (NOT USED)"},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
# include "DNA_anim_types.h"
@@ -107,6 +148,8 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
# include "DEG_depsgraph_build.h"
# include "DEG_depsgraph_query.h"
+# include "ED_asset.h"
+
# include "WM_api.h"
void rna_ID_override_library_property_operation_refname_get(PointerRNA *ptr, char *value)
@@ -303,7 +346,7 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_Screen) {
return ID_SCR;
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
if (base_type == &RNA_Simulation) {
return ID_SIM;
}
@@ -411,7 +454,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_SCR:
return &RNA_Screen;
case ID_SIM:
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
return &RNA_Simulation;
# else
return &RNA_ID;
@@ -534,6 +577,22 @@ static ID *rna_ID_copy(ID *id, Main *bmain)
return newid;
}
+static void rna_ID_asset_mark(ID *id, bContext *C)
+{
+ if (ED_asset_mark_id(C, id)) {
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL);
+ }
+}
+
+static void rna_ID_asset_clear(ID *id)
+{
+ if (ED_asset_clear_id(id)) {
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL);
+ }
+}
+
static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
{
if (!ID_IS_OVERRIDABLE_LIBRARY(id)) {
@@ -549,9 +608,88 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
if (remap_local_usages) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
+
+ WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
+
return local_id;
}
+static ID *rna_ID_override_hierarchy_create(
+ ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_reference)
+{
+ if (!ID_IS_OVERRIDABLE_LIBRARY(id)) {
+ return NULL;
+ }
+
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ ID *id_root_override = NULL;
+ BKE_lib_override_library_create(bmain, scene, view_layer, id, id_reference, &id_root_override);
+
+ WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
+
+ return id_root_override;
+}
+
+static void rna_ID_override_template_create(ID *id, ReportList *reports)
+{
+ if (!U.experimental.use_override_templates) {
+ BKE_report(reports, RPT_ERROR, "Override template experimental feature is disabled");
+ return;
+ }
+ if (ID_IS_LINKED(id)) {
+ BKE_report(reports, RPT_ERROR, "Unable to create override template for linked data-blocks");
+ return;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ BKE_report(
+ reports, RPT_ERROR, "Unable to create override template for overridden data-blocks");
+ return;
+ }
+ BKE_lib_override_library_template_create(id);
+}
+
+static IDOverrideLibraryProperty *rna_ID_override_library_properties_add(
+ IDOverrideLibrary *override_library, ReportList *reports, const char rna_path[])
+{
+ bool created;
+ IDOverrideLibraryProperty *result = BKE_lib_override_library_property_get(
+ override_library, rna_path, &created);
+
+ if (!created) {
+ BKE_report(reports, RPT_DEBUG, "No new override property created, property already exists");
+ }
+
+ return result;
+}
+
+static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_operations_add(
+ IDOverrideLibraryProperty *override_property,
+ ReportList *reports,
+ int operation,
+ const char *subitem_refname,
+ const char *subitem_locname,
+ int subitem_refindex,
+ int subitem_locindex)
+{
+ bool created;
+ bool strict;
+ IDOverrideLibraryPropertyOperation *result = BKE_lib_override_library_property_operation_get(
+ override_property,
+ operation,
+ subitem_refname,
+ subitem_locname,
+ subitem_refindex,
+ subitem_locindex,
+ false,
+ &strict,
+ &created);
+ if (!created) {
+ BKE_report(reports, RPT_DEBUG, "No new override operation created, operation already exists");
+ }
+ return result;
+}
+
static void rna_ID_update_tag(ID *id, Main *bmain, ReportList *reports, int flag)
{
/* XXX, new function for this! */
@@ -1019,7 +1157,7 @@ static void rna_ImagePreview_icon_reload(PreviewImage *prv)
static PointerRNA rna_IDPreview_get(PointerRNA *ptr)
{
ID *id = (ID *)ptr->data;
- PreviewImage *prv_img = BKE_previewimg_id_ensure(id);
+ PreviewImage *prv_img = BKE_previewimg_id_get(id);
return rna_pointer_inherit_refine(ptr, &RNA_ImagePreview, prv_img);
}
@@ -1267,47 +1405,6 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
- static const EnumPropertyItem override_library_property_operation_items[] = {
- {IDOVERRIDE_LIBRARY_OP_NOOP,
- "NOOP",
- 0,
- "No-Op",
- "Does nothing, prevents adding actual overrides (NOT USED)"},
- {IDOVERRIDE_LIBRARY_OP_REPLACE,
- "REPLACE",
- 0,
- "Replace",
- "Replace value of reference by overriding one"},
- {IDOVERRIDE_LIBRARY_OP_ADD,
- "DIFF_ADD",
- 0,
- "Differential",
- "Stores and apply difference between reference and local value (NOT USED)"},
- {IDOVERRIDE_LIBRARY_OP_SUBTRACT,
- "DIFF_SUB",
- 0,
- "Differential",
- "Stores and apply difference between reference and local value (NOT USED)"},
- {IDOVERRIDE_LIBRARY_OP_MULTIPLY,
- "FACT_MULTIPLY",
- 0,
- "Factor",
- "Stores and apply multiplication factor between reference and local value (NOT USED)"},
- {IDOVERRIDE_LIBRARY_OP_INSERT_AFTER,
- "INSERT_AFTER",
- 0,
- "Insert After",
- "Insert a new item into collection after the one referenced in subitem_reference_name or "
- "_index"},
- {IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE,
- "INSERT_BEFORE",
- 0,
- "Insert Before",
- "Insert a new item into collection after the one referenced in subitem_reference_name or "
- "_index (NOT USED)"},
- {0, NULL, 0, NULL, NULL},
- };
-
static const EnumPropertyItem override_library_property_flag_items[] = {
{IDOVERRIDE_LIBRARY_FLAG_MANDATORY,
"MANDATORY",
@@ -1329,7 +1426,7 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna)
prop = RNA_def_enum(srna,
"operation",
- override_library_property_operation_items,
+ rna_enum_override_library_property_operation_items,
IDOVERRIDE_LIBRARY_OP_REPLACE,
"Operation",
"What override operation is performed");
@@ -1386,6 +1483,66 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */
}
+static void rna_def_ID_override_library_property_operations(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "IDOverrideLibraryPropertyOperations");
+ srna = RNA_def_struct(brna, "IDOverrideLibraryPropertyOperations", NULL);
+ RNA_def_struct_sdna(srna, "IDOverrideLibraryProperty");
+ RNA_def_struct_ui_text(srna, "Override Operations", "Collection of override operations");
+
+ /* Add Property */
+ func = RNA_def_function(srna, "add", "rna_ID_override_library_property_operations_add");
+ RNA_def_function_ui_description(func, "Add a new operation");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_enum(func,
+ "operation",
+ rna_enum_override_library_property_operation_items,
+ IDOVERRIDE_LIBRARY_OP_REPLACE,
+ "Operation",
+ "What override operation is performed");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_string(func,
+ "subitem_reference_name",
+ NULL,
+ INT_MAX,
+ "Subitem Reference Name",
+ "Used to handle insertions into collection");
+ parm = RNA_def_string(func,
+ "subitem_local_name",
+ NULL,
+ INT_MAX,
+ "Subitem Local Name",
+ "Used to handle insertions into collection");
+ parm = RNA_def_int(func,
+ "subitem_reference_index",
+ -1,
+ -1,
+ INT_MAX,
+ "Subitem Reference Index",
+ "Used to handle insertions into collection",
+ -1,
+ INT_MAX);
+ parm = RNA_def_int(func,
+ "subitem_local_index",
+ -1,
+ -1,
+ INT_MAX,
+ "Subitem Local Index",
+ "Used to handle insertions into collection",
+ -1,
+ INT_MAX);
+ parm = RNA_def_pointer(func,
+ "property",
+ "IDOverrideLibraryPropertyOperation",
+ "New Operation",
+ "Created operation");
+ RNA_def_function_return(func, parm);
+}
+
static void rna_def_ID_override_library_property(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1405,18 +1562,47 @@ static void rna_def_ID_override_library_property(BlenderRNA *brna)
"RNA path leading to that property, from owning ID");
RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */
- RNA_def_collection(srna,
- "operations",
- "IDOverrideLibraryPropertyOperation",
- "Operations",
- "List of overriding operations for a property");
+ prop = RNA_def_collection(srna,
+ "operations",
+ "IDOverrideLibraryPropertyOperation",
+ "Operations",
+ "List of overriding operations for a property");
+ rna_def_ID_override_library_property_operations(brna, prop);
rna_def_ID_override_library_property_operation(brna);
}
+static void rna_def_ID_override_library_properties(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "IDOverrideLibraryProperties");
+ srna = RNA_def_struct(brna, "IDOverrideLibraryProperties", NULL);
+ RNA_def_struct_sdna(srna, "IDOverrideLibrary");
+ RNA_def_struct_ui_text(srna, "Override Properties", "Collection of override properties");
+
+ /* Add Property */
+ func = RNA_def_function(srna, "add", "rna_ID_override_library_properties_add");
+ RNA_def_function_ui_description(
+ func, "Add a property to the override library when it doesn't exist yet");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func,
+ "property",
+ "IDOverrideLibraryProperty",
+ "New Property",
+ "Newly created override property or existing one");
+ RNA_def_function_return(func, parm);
+ parm = RNA_def_string(
+ func, "rna_path", NULL, 256, "RNA Path", "RNA-Path of the property to add");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+}
+
static void rna_def_ID_override_library(BlenderRNA *brna)
{
StructRNA *srna;
+ PropertyRNA *prop;
srna = RNA_def_struct(brna, "IDOverrideLibrary", NULL);
RNA_def_struct_ui_text(
@@ -1425,11 +1611,12 @@ static void rna_def_ID_override_library(BlenderRNA *brna)
RNA_def_pointer(
srna, "reference", "ID", "Reference ID", "Linked ID used as reference by this override");
- RNA_def_collection(srna,
- "properties",
- "IDOverrideLibraryProperty",
- "Properties",
- "List of overridden properties");
+ prop = RNA_def_collection(srna,
+ "properties",
+ "IDOverrideLibraryProperty",
+ "Properties",
+ "List of overridden properties");
+ rna_def_ID_override_library_properties(brna, prop);
rna_def_ID_override_library_property(brna);
}
@@ -1540,12 +1727,12 @@ static void rna_def_ID(BlenderRNA *brna)
srna, "override_library", "IDOverrideLibrary", "Library Override", "Library override data");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- prop = RNA_def_pointer(
- srna,
- "preview",
- "ImagePreview",
- "Preview",
- "Preview image and icon of this data-block (None if not supported for this type of data)");
+ prop = RNA_def_pointer(srna,
+ "preview",
+ "ImagePreview",
+ "Preview",
+ "Preview image and icon of this data-block (always None if not supported "
+ "for this type of data)");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_pointer_funcs(prop, "rna_IDPreview_get", NULL, NULL, NULL);
@@ -1567,6 +1754,18 @@ static void rna_def_ID(BlenderRNA *brna)
parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID");
RNA_def_function_return(func, parm);
+ func = RNA_def_function(srna, "asset_mark", "rna_ID_asset_mark");
+ RNA_def_function_ui_description(
+ func,
+ "Enable easier reuse of the data-block through the Asset Browser, with the help of "
+ "customizable metadata (like previews, descriptions and tags)");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+
+ func = RNA_def_function(srna, "asset_clear", "rna_ID_asset_clear");
+ RNA_def_function_ui_description(
+ func,
+ "Delete all asset metadata and turn the asset data-block back into a normal data-block");
+
func = RNA_def_function(srna, "override_create", "rna_ID_override_create");
RNA_def_function_ui_description(func,
"Create an overridden local copy of this linked data-block (not "
@@ -1581,6 +1780,34 @@ static void rna_def_ID(BlenderRNA *brna)
"Whether local usages of the linked ID should be remapped to the new "
"library override of it");
+ func = RNA_def_function(srna, "override_hierarchy_create", "rna_ID_override_hierarchy_create");
+ RNA_def_function_ui_description(
+ func,
+ "Create an overridden local copy of this linked data-block, and most of its dependencies "
+ "when it is a Collection or and Object");
+ RNA_def_function_flag(func, FUNC_USE_MAIN);
+ parm = RNA_def_pointer(func, "id", "ID", "", "New overridden local copy of the root ID");
+ RNA_def_function_return(func, parm);
+ parm = RNA_def_pointer(
+ func, "scene", "Scene", "", "In which scene the new overrides should be instantiated");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "view_layer",
+ "ViewLayer",
+ "",
+ "In which view layer the new overrides should be instantiated");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_pointer(func,
+ "reference",
+ "ID",
+ "",
+ "Another ID (usually an Object or Collection) used to decide where to "
+ "instantiate the new overrides");
+
+ func = RNA_def_function(srna, "override_template_create", "rna_ID_override_template_create");
+ RNA_def_function_ui_description(func, "Create an override template for this ID");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+
func = RNA_def_function(srna, "user_clear", "rna_ID_user_clear");
RNA_def_function_ui_description(func,
"Clear the user count of a data-block so its not saved, "
@@ -1643,6 +1870,13 @@ static void rna_def_ID(BlenderRNA *brna)
"e.g. when calling :class:`bpy.types.Scene.update`");
RNA_def_enum_flag(func, "refresh", update_flag_items, 0, "", "Type of updates to perform");
+ func = RNA_def_function(srna, "preview_ensure", "BKE_previewimg_id_ensure");
+ RNA_def_function_ui_description(func,
+ "Ensure that this ID has preview data (if ID type supports it)");
+ parm = RNA_def_pointer(
+ func, "preview_image", "ImagePreview", "", "The existing or created preview");
+ RNA_def_function_return(func, parm);
+
# ifdef WITH_PYTHON
RNA_def_struct_register_funcs(srna, NULL, NULL, "rna_ID_instance");
# endif
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index f94dc38ddfe..948fef1b51e 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -1189,6 +1189,24 @@ PropertyUnit RNA_property_unit(PropertyRNA *prop)
return RNA_SUBTYPE_UNIT(RNA_property_subtype(prop));
}
+PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop)
+{
+ PropertyRNA *rna_prop = rna_ensure_property(prop);
+
+ switch (rna_prop->type) {
+ case PROP_INT: {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)rna_prop;
+ return iprop->ui_scale_type;
+ }
+ case PROP_FLOAT: {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_prop;
+ return fprop->ui_scale_type;
+ }
+ default:
+ return PROP_SCALE_LINEAR;
+ }
+}
+
int RNA_property_flag(PropertyRNA *prop)
{
return rna_ensure_property(prop)->flag;
@@ -1623,35 +1641,35 @@ void RNA_property_enum_items_ex(bContext *C,
*r_free = false;
- if (!use_static && eprop->item_fn && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
- const EnumPropertyItem *item;
+ if (!use_static && (eprop->item_fn != NULL)) {
+ const bool no_context = (prop->flag & PROP_ENUM_NO_CONTEXT) ||
+ ((ptr->type->flag & STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID) &&
+ (ptr->owner_id == NULL));
+ if (C != NULL || no_context) {
+ const EnumPropertyItem *item;
- if (prop->flag & PROP_ENUM_NO_CONTEXT) {
- item = eprop->item_fn(NULL, ptr, prop, r_free);
- }
- else {
- item = eprop->item_fn(C, ptr, prop, r_free);
- }
+ item = eprop->item_fn(no_context ? NULL : C, ptr, prop, r_free);
- /* any callbacks returning NULL should be fixed */
- BLI_assert(item != NULL);
+ /* any callbacks returning NULL should be fixed */
+ BLI_assert(item != NULL);
- if (r_totitem) {
- int tot;
- for (tot = 0; item[tot].identifier; tot++) {
- /* pass */
+ if (r_totitem) {
+ int tot;
+ for (tot = 0; item[tot].identifier; tot++) {
+ /* pass */
+ }
+ *r_totitem = tot;
}
- *r_totitem = tot;
- }
- *r_item = item;
- }
- else {
- *r_item = eprop->item;
- if (r_totitem) {
- *r_totitem = eprop->totitem;
+ *r_item = item;
+ return;
}
}
+
+ *r_item = eprop->item;
+ if (r_totitem) {
+ *r_totitem = eprop->totitem;
+ }
}
void RNA_property_enum_items(bContext *C,
@@ -1753,42 +1771,42 @@ void RNA_property_enum_items_gettexted_all(bContext *C,
*r_totitem = eprop->totitem;
}
- if (eprop->item_fn && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
- const EnumPropertyItem *item;
- int i;
- bool free = false;
+ if (eprop->item_fn != NULL) {
+ const bool no_context = (prop->flag & PROP_ENUM_NO_CONTEXT) ||
+ ((ptr->type->flag & STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID) &&
+ (ptr->owner_id == NULL));
+ if (C != NULL || no_context) {
+ const EnumPropertyItem *item;
+ int i;
+ bool free = false;
- if (prop->flag & PROP_ENUM_NO_CONTEXT) {
- item = eprop->item_fn(NULL, ptr, prop, &free);
- }
- else {
- item = eprop->item_fn(C, ptr, prop, &free);
- }
+ item = eprop->item_fn(no_context ? NULL : NULL, ptr, prop, &free);
- /* any callbacks returning NULL should be fixed */
- BLI_assert(item != NULL);
+ /* any callbacks returning NULL should be fixed */
+ BLI_assert(item != NULL);
- for (i = 0; i < eprop->totitem; i++) {
- bool exists = false;
- int i_fixed;
+ for (i = 0; i < eprop->totitem; i++) {
+ bool exists = false;
+ int i_fixed;
- /* Items that do not exist on list are returned,
- * but have their names/identifiers NULL'ed out. */
- for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
- if (STREQ(item[i_fixed].identifier, item_array[i].identifier)) {
- exists = true;
- break;
+ /* Items that do not exist on list are returned,
+ * but have their names/identifiers NULL'ed out. */
+ for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
+ if (STREQ(item[i_fixed].identifier, item_array[i].identifier)) {
+ exists = true;
+ break;
+ }
}
- }
- if (!exists) {
- item_array[i].name = NULL;
- item_array[i].identifier = "";
+ if (!exists) {
+ item_array[i].name = NULL;
+ item_array[i].identifier = "";
+ }
}
- }
- if (free) {
- MEM_freeN((void *)item);
+ if (free) {
+ MEM_freeN((void *)item);
+ }
}
}
@@ -6097,13 +6115,25 @@ char *RNA_path_full_ID_py(Main *bmain, ID *id)
path = "";
}
- char id_esc[(sizeof(id->name) - 2) * 2];
+ char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4];
+ if (id->lib != NULL) {
+ int ofs = 0;
+ memcpy(lib_filepath_esc, ", \"", 3);
+ ofs += 3;
+ ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc));
+ memcpy(lib_filepath_esc + ofs, "\"", 2);
+ }
+ else {
+ lib_filepath_esc[0] = '\0';
+ }
+ char id_esc[(sizeof(id->name) - 2) * 2];
BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc));
- return BLI_sprintfN("bpy.data.%s[\"%s\"]%s%s",
+ return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s",
BKE_idtype_idcode_to_name_plural(GS(id->name)),
id_esc,
+ lib_filepath_esc,
path[0] ? "." : "",
path);
}
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index 69ddcee60fa..fb9f5e0292e 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -407,8 +407,10 @@ static void rna_def_dopesheet(BlenderRNA *brna)
/* Multi-word fuzzy search option for name/text filters */
prop = RNA_def_property(srna, "use_multi_word_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ADS_FLAG_FUZZY_NAMES);
- RNA_def_property_ui_text(
- prop, "Multi-Word Fuzzy Filter", "Perform fuzzy/multi-word matching (WARNING: May be slow)");
+ RNA_def_property_ui_text(prop,
+ "Multi-Word Fuzzy Filter",
+ "Perform fuzzy/multi-word matching.\n"
+ "Warning: May be slow");
RNA_def_property_ui_icon(prop, ICON_SORTALPHA, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
diff --git a/source/blender/makesrna/intern/rna_action_api.c b/source/blender/makesrna/intern/rna_action_api.c
index 48da32afba5..308a34f9cf1 100644
--- a/source/blender/makesrna/intern/rna_action_api.c
+++ b/source/blender/makesrna/intern/rna_action_api.c
@@ -41,10 +41,32 @@
# include "DNA_anim_types.h"
# include "DNA_curve_types.h"
+static void rna_Action_flip_with_pose(bAction *act, ReportList *reports, Object *ob)
+{
+ if (ob->type != OB_ARMATURE) {
+ BKE_report(reports, RPT_ERROR, "Only armature objects are supported");
+ return;
+ }
+ BKE_action_flip_with_pose(act, ob);
+
+ /* Only for redraw. */
+ WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+}
+
#else
-void RNA_api_action(StructRNA *UNUSED(srna))
+void RNA_api_action(StructRNA *srna)
{
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ func = RNA_def_function(srna, "flip_with_pose", "rna_Action_flip_with_pose");
+ RNA_def_function_ui_description(func, "Flip the action around the X axis using a pose");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+
+ parm = RNA_def_pointer(
+ func, "object", "Object", "", "The reference armature object to use when flipping");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index 554f04ca23c..c8fccfc27f8 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -674,7 +674,7 @@ static bool rna_Armature_is_editmode_get(PointerRNA *ptr)
return (arm->edbo != NULL);
}
-static void rna_Armature_transform(bArmature *arm, float *mat)
+static void rna_Armature_transform(bArmature *arm, float mat[16])
{
ED_armature_transform(arm, (const float(*)[4])mat, true);
}
@@ -1537,6 +1537,16 @@ static void rna_def_armature(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
+ prop = RNA_def_property(srna, "axes_position", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "axes_position");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_range(prop, 0.0, 1.0, 10, 1);
+ RNA_def_property_ui_text(prop,
+ "Axes Position",
+ "The position for the axes on the bone. Increasing the value moves it "
+ "closer to the tip; decreasing moves it closer to the root");
+ RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
+
prop = RNA_def_property(srna, "show_names", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_DRAWNAMES);
RNA_def_property_ui_text(prop, "Display Names", "Display bone names");
diff --git a/source/blender/makesrna/intern/rna_armature_api.c b/source/blender/makesrna/intern/rna_armature_api.c
index 36aa0401c7d..a02f55667e3 100644
--- a/source/blender/makesrna/intern/rna_armature_api.c
+++ b/source/blender/makesrna/intern/rna_armature_api.c
@@ -44,7 +44,7 @@ static void rna_EditBone_align_roll(EditBone *ebo, float no[3])
ebo->roll = ED_armature_ebone_roll_to_vector(ebo, no, false);
}
-static float rna_Bone_do_envelope(Bone *bone, float *vec)
+static float rna_Bone_do_envelope(Bone *bone, float vec[3])
{
float scale = (bone->flag & BONE_MULT_VG_ENV) == BONE_MULT_VG_ENV ? bone->weight : 1.0f;
return distfactor_to_bone(vec,
@@ -56,11 +56,11 @@ static float rna_Bone_do_envelope(Bone *bone, float *vec)
}
static void rna_Bone_convert_local_to_pose(Bone *bone,
- float *r_matrix,
- float *matrix,
- float *matrix_local,
- float *parent_matrix,
- float *parent_matrix_local,
+ float r_matrix[16],
+ float matrix[16],
+ float matrix_local[16],
+ float parent_matrix[16],
+ float parent_matrix_local[16],
bool invert)
{
BoneParentTransform bpt;
@@ -89,14 +89,14 @@ static void rna_Bone_convert_local_to_pose(Bone *bone,
BKE_bone_parent_transform_apply(&bpt, (float(*)[4])matrix, (float(*)[4])r_matrix);
}
-static void rna_Bone_MatrixFromAxisRoll(float *axis, float roll, float *r_matrix)
+static void rna_Bone_MatrixFromAxisRoll(float axis[3], float roll, float r_matrix[9])
{
vec_roll_to_mat3(axis, roll, (float(*)[3])r_matrix);
}
-static void rna_Bone_AxisRollFromMatrix(float *matrix,
- float *axis_override,
- float *r_axis,
+static void rna_Bone_AxisRollFromMatrix(float matrix[9],
+ float axis_override[3],
+ float r_axis[3],
float *r_roll)
{
float mat[3][3];
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index 7976df3e4e4..a256002ffc1 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -49,6 +49,19 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
+ {CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""},
+ {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"},
+ {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"},
+ {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"},
+ {CD_PROP_COLOR, "FLOAT_COLOR", 0, "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"},
+ {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
+ {0, NULL, 0, NULL, NULL},
+};
+
const EnumPropertyItem rna_enum_attribute_domain_items[] = {
/* Not implement yet */
// {ATTR_DOMAIN_GEOMETRY, "GEOMETRY", 0, "Geometry", "Attribute on (whole) geometry"},
@@ -58,7 +71,7 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = {
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"},
/* Not implement yet */
// {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"},
- {ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", "Attribute on hair curve"},
+ {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
@@ -68,6 +81,7 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = {
{ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
{ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"},
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"},
+ {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index e7daa55af6c..7e1d513502c 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1617,6 +1617,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
prop, "Stroke Extension", "Strokes end extension for closing gaps, use zero to disable");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+ /* Number of pixels to dilate fill area. */
+ prop = RNA_def_property(srna, "dilate", PROP_INT, PROP_PIXEL);
+ RNA_def_property_int_sdna(prop, NULL, "dilate_pixels");
+ RNA_def_property_range(prop, 0, 20);
+ RNA_def_property_int_default(prop, 1);
+ RNA_def_property_ui_text(prop, "Dilate", "Number of pixels to dilate fill area");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
/* Flags */
prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE);
diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c
index 2bc00dd5af5..9e57368f8f9 100644
--- a/source/blender/makesrna/intern/rna_cloth.c
+++ b/source/blender/makesrna/intern/rna_cloth.c
@@ -652,6 +652,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_UNIT_MASS);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_text(prop, "Vertex Mass", "The mass of each vertex on the cloth material");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "vertex_group_mass", PROP_STRING, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index 206ebc2cb14..54f9a93d90a 100644
--- a/source/blender/makesrna/intern/rna_color.c
+++ b/source/blender/makesrna/intern/rna_color.c
@@ -650,7 +650,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain,
seq->strip->proxy->anim = NULL;
}
- SEQ_relations_invalidate_cache_preprocessed(scene, seq);
+ SEQ_relations_invalidate_cache_raw(scene, seq);
}
else {
SEQ_ALL_BEGIN (scene->ed, seq) {
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 843cb326be2..b363dcd4ba9 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -1923,7 +1923,8 @@ static void rna_def_constraint_follow_path(BlenderRNA *brna)
prop = RNA_def_property(srna, "offset_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "offset_fac");
- RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01, 3);
RNA_def_property_ui_text(
prop, "Offset Factor", "Percentage value defining target position along length of curve");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
@@ -2597,6 +2598,12 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Maximum Z", "Highest Z value to allow");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+ prop = RNA_def_property(srna, "euler_order", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "euler_order");
+ RNA_def_property_enum_items(prop, euler_order_items);
+ RNA_def_property_ui_text(prop, "Euler Order", "Explicitly specify the euler rotation order");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c
index 3e90b4bd9d4..dd7d50a80ba 100644
--- a/source/blender/makesrna/intern/rna_curve.c
+++ b/source/blender/makesrna/intern/rna_curve.c
@@ -357,9 +357,8 @@ static void rna_Curve_dimension_set(PointerRNA *ptr, int value)
}
else {
cu->flag &= ~CU_3D;
+ BKE_curve_dimension_update(cu);
}
-
- BKE_curve_curve_dimension_update(cu);
}
static const EnumPropertyItem *rna_Curve_fill_mode_itemf(bContext *UNUSED(C),
@@ -721,10 +720,6 @@ static Nurb *rna_Curve_spline_new(Curve *cu, int type)
nu->resolv = cu->resolv;
nu->flag = CU_SMOOTH;
- if ((cu->flag & CU_3D) == 0) {
- nu->flag |= CU_2D;
- }
-
BLI_addtail(BKE_curve_nurbs_get(cu), nu);
return nu;
@@ -1035,6 +1030,14 @@ static void rna_def_path(BlenderRNA *UNUSED(brna), StructRNA *srna)
RNA_def_property_ui_text(prop, "Follow", "Make curve path children to rotate along the path");
RNA_def_property_update(prop, 0, "rna_Curve_update_data");
+ prop = RNA_def_property(srna, "use_path_clamp", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_PATH_CLAMP);
+ RNA_def_property_ui_text(
+ prop,
+ "Clamp",
+ "Clamp the curve path children so they can't travel past the start/end point of the curve");
+ RNA_def_property_update(prop, 0, "rna_Curve_update_data");
+
prop = RNA_def_property(srna, "use_stretch", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_STRETCH);
RNA_def_property_ui_text(prop,
@@ -1373,7 +1376,7 @@ static void rna_def_charinfo(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
// RNA_def_property_int_sdna(prop, NULL, "mat_nr");
- RNA_def_property_ui_text(prop, "Material Index", "");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this character");
RNA_def_property_int_funcs(prop,
"rna_ChariInfo_material_index_get",
"rna_ChariInfo_material_index_set",
@@ -1834,7 +1837,7 @@ static void rna_def_curve(BlenderRNA *brna)
prop = RNA_def_property(srna, "texspace_location", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 3);
- RNA_def_property_ui_text(prop, "Texture Space Location", "Texture space location");
+ RNA_def_property_ui_text(prop, "Texture Space Location", "");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_editable_func(prop, "rna_Curve_texspace_editable");
RNA_def_property_float_funcs(
@@ -1844,7 +1847,7 @@ static void rna_def_curve(BlenderRNA *brna)
prop = RNA_def_property(srna, "texspace_size", PROP_FLOAT, PROP_XYZ);
RNA_def_property_array(prop, 3);
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
- RNA_def_property_ui_text(prop, "Texture Space Size", "Texture space size");
+ RNA_def_property_ui_text(prop, "Texture Space Size", "");
RNA_def_property_editable_func(prop, "rna_Curve_texspace_editable");
RNA_def_property_float_funcs(
prop, "rna_Curve_texspace_size_get", "rna_Curve_texspace_size_set", NULL);
@@ -2065,7 +2068,7 @@ static void rna_def_curve_nurb(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "mat_nr");
- RNA_def_property_ui_text(prop, "Material Index", "");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this curve");
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Curve_material_index_range");
RNA_def_property_update(prop, 0, "rna_Curve_update_data");
diff --git a/source/blender/makesrna/intern/rna_curve_api.c b/source/blender/makesrna/intern/rna_curve_api.c
index 94fdb130026..beea607e8c0 100644
--- a/source/blender/makesrna/intern/rna_curve_api.c
+++ b/source/blender/makesrna/intern/rna_curve_api.c
@@ -35,7 +35,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Curve_transform(Curve *cu, float *mat, bool shape_keys)
+static void rna_Curve_transform(Curve *cu, float mat[16], bool shape_keys)
{
BKE_curve_transform(cu, (const float(*)[4])mat, shape_keys, true);
diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c
index bb54d55f8bd..b3ab8cc15a2 100644
--- a/source/blender/makesrna/intern/rna_curveprofile.c
+++ b/source/blender/makesrna/intern/rna_curveprofile.c
@@ -137,7 +137,7 @@ static void rna_CurveProfile_remove_point(CurveProfile *profile,
static void rna_CurveProfile_evaluate(struct CurveProfile *profile,
ReportList *reports,
float length_portion,
- float *location)
+ float location[2])
{
if (!profile->table) {
BKE_report(reports, RPT_ERROR, "CurveProfile table not initialized, call initialize()");
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index 08f52be4257..9b9d561603b 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -1754,6 +1754,28 @@ void RNA_def_property_ui_range(
}
}
+void RNA_def_property_ui_scale_type(PropertyRNA *prop, PropertyScaleType ui_scale_type)
+{
+ StructRNA *srna = DefRNA.laststruct;
+
+ switch (prop->type) {
+ case PROP_INT: {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
+ iprop->ui_scale_type = ui_scale_type;
+ break;
+ }
+ case PROP_FLOAT: {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
+ fprop->ui_scale_type = ui_scale_type;
+ break;
+ }
+ default:
+ CLOG_ERROR(&LOG, "\"%s.%s\", invalid type for scale.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ break;
+ }
+}
+
void RNA_def_property_range(PropertyRNA *prop, double min, double max)
{
StructRNA *srna = DefRNA.laststruct;
@@ -2410,6 +2432,10 @@ void RNA_def_property_int_sdna(PropertyRNA *prop, const char *structname, const
iprop->softmin = -10000; /* rather arbitrary .. */
iprop->softmax = 10000;
}
+ else if (dp->dnatype && STREQ(dp->dnatype, "int8_t")) {
+ iprop->hardmin = iprop->softmin = INT8_MIN;
+ iprop->hardmax = iprop->softmax = INT8_MAX;
+ }
if (prop->subtype == PROP_UNSIGNED || prop->subtype == PROP_PERCENTAGE ||
prop->subtype == PROP_FACTOR) {
diff --git a/source/blender/makesrna/intern/rna_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c
index 36d39c5c201..85071e8cd19 100644
--- a/source/blender/makesrna/intern/rna_depsgraph.c
+++ b/source/blender/makesrna/intern/rna_depsgraph.c
@@ -505,11 +505,10 @@ static void rna_def_depsgraph_instance(BlenderRNA *brna)
PropertyRNA *prop;
srna = RNA_def_struct(brna, "DepsgraphObjectInstance", NULL);
- RNA_def_struct_ui_text(
- srna,
- "Dependency Graph Object Instance",
- "Extended information about dependency graph object iterator "
- "(WARNING: all data here is *evaluated* one, not original .blend IDs...)");
+ RNA_def_struct_ui_text(srna,
+ "Dependency Graph Object Instance",
+ "Extended information about dependency graph object iterator "
+ "(Warning: All data here is 'evaluated' one, not original .blend IDs)");
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Object");
@@ -773,7 +772,7 @@ static void rna_def_depsgraph(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Object Instances",
"All object instances to display or render "
- "(WARNING: only use this as an iterator, never as a sequence, "
+ "(Warning: Only use this as an iterator, never as a sequence, "
"and do not keep any references to its items)");
prop = RNA_def_property(srna, "updates", PROP_COLLECTION, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index 1c7f6ef5cb3..1b89d866aba 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -1160,6 +1160,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
/* define common props */
prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop,
"Additive",
"Values generated by this modifier are applied on top of "
@@ -1168,12 +1169,14 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, generator_mode_items);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Mode", "Type of generator to use");
RNA_def_property_update(
prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_verify_data_update");
/* order of the polynomial */
prop = RNA_def_property(srna, "poly_order", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Polynomial Order",
@@ -1184,6 +1187,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
/* coefficients array */
prop = RNA_def_property(srna, "coefficients", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_array(prop, 32);
RNA_def_property_flag(prop, PROP_DYNAMIC);
RNA_def_property_dynamic_array_funcs(prop, "rna_FModifierGenerator_coefficients_get_length");
@@ -1221,25 +1225,30 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna)
/* coefficients */
prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Amplitude", "Scale factor determining the maximum/minimum values");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "phase_multiplier", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Phase Multiple", "Scale factor determining the 'speed' of the function");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "phase_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Phase Offset", "Constant factor to offset time by for function");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "value_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Value Offset", "Constant factor to offset values by");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
/* flags */
prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE);
RNA_def_property_ui_text(prop,
"Additive",
@@ -1248,6 +1257,7 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna)
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "function_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "Type", "Type of built-in function to use");
@@ -1271,17 +1281,20 @@ static void rna_def_fmodifier_envelope_ctrl(BlenderRNA *brna)
*/
prop = RNA_def_property(srna, "min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "min");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum Value", "Lower bound of envelope at this control-point");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "max");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum Value", "Upper bound of envelope at this control-point");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
/* Frame */
prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_TIME);
RNA_def_property_float_sdna(prop, NULL, "time");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Frame", "Frame this control-point occurs on");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
@@ -1340,6 +1353,7 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna)
/* Collections */
prop = RNA_def_property(srna, "control_points", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "data", "totvert");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_struct_type(prop, "FModifierEnvelopeControlPoint");
RNA_def_property_ui_text(
prop, "Control Points", "Control points defining the shape of the envelope");
@@ -1348,18 +1362,21 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna)
/* Range Settings */
prop = RNA_def_property(srna, "reference_value", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "midval");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Reference Value", "Value that envelope's influence is centered around / based on");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "default_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "min");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Default Minimum", "Lower distance from Reference Value for 1:1 default influence");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "default_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "max");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Default Maximum", "Upper distance from Reference Value for 1:1 default influence");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
@@ -1395,12 +1412,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna)
/* before */
prop = RNA_def_property(srna, "mode_before", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "before_mode");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "Before Mode", "Cycling mode to use before first keyframe");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "cycles_before", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_int_sdna(prop, NULL, "before_cycles");
RNA_def_property_ui_text(
prop,
@@ -1410,12 +1429,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna)
/* after */
prop = RNA_def_property(srna, "mode_after", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "after_mode");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "After Mode", "Cycling mode to use after last keyframe");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "cycles_after", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_int_sdna(prop, NULL, "after_cycles");
RNA_def_property_ui_text(prop,
"After Cycles",
@@ -1450,26 +1471,31 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_min_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMIN);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum X", "Use the minimum X value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_min_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMIN);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum Y", "Use the minimum Y value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_max_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMAX);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum X", "Use the maximum X value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_max_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMAX);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum Y", "Use the maximum Y value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "min_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.xmin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_minx_set", "rna_FModifierLimits_minx_range");
RNA_def_property_ui_text(prop, "Minimum X", "Lowest X value to allow");
@@ -1477,6 +1503,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "min_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.ymin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_miny_set", "rna_FModifierLimits_miny_range");
RNA_def_property_ui_text(prop, "Minimum Y", "Lowest Y value to allow");
@@ -1484,6 +1511,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "max_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.xmax");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_maxx_set", "rna_FModifierLimits_maxx_range");
RNA_def_property_ui_text(prop, "Maximum X", "Highest X value to allow");
@@ -1491,6 +1519,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "max_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.ymax");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_maxy_set", "rna_FModifierLimits_maxy_range");
RNA_def_property_ui_text(prop, "Maximum Y", "Highest Y value to allow");
@@ -1519,16 +1548,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "modification");
RNA_def_property_enum_items(prop, prop_modification_items);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Blend Type", "Method of modifying the existing F-Curve");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "size");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Scale", "Scaling (in time) of the noise");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "strength");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Strength",
@@ -1537,16 +1569,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna)
prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "phase");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Phase", "A random seed for the noise effect");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "offset");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Offset", "Time offset for the noise effect");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "depth", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "depth");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Depth", "Amount of fine level detail present in the noise");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
}
@@ -1569,11 +1604,13 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
/* properties */
prop = RNA_def_property(srna, "frame_step", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "step_size");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Step Size", "Number of frames to hold each value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "frame_offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "offset");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop,
"Offset",
"Reference number of frames before frames get held "
@@ -1582,18 +1619,21 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_frame_start", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_BEFORE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use Start Frame", "Restrict modifier to only act after its 'start' frame");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_frame_end", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_AFTER);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use End Frame", "Restrict modifier to only act before its 'end' frame");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "start_frame");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop,
NULL,
"rna_FModifierStepped_frame_start_set",
@@ -1604,6 +1644,7 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "end_frame");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierStepped_frame_end_set", "rna_FModifierStepped_end_frame_range");
RNA_def_property_ui_text(
@@ -1641,12 +1682,14 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
+ // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_show_expanded_set");
RNA_def_property_ui_text(prop, "Expanded", "F-Curve Modifier's panel is expanded in UI");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_MUTED);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Enabled", "Enable F-Curve modifier evaluation");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1);
@@ -1654,6 +1697,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FMODIFIER_FLAG_DISABLED);
+ // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Disabled", "F-Curve Modifier has invalid settings and will not be evaluated");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
@@ -1661,6 +1705,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* TODO: setting this to true must ensure that all others in stack are turned off too... */
prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_ACTIVE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Active", "F-Curve modifier will show settings in the editor");
RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_active_set");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_active_update");
@@ -1669,6 +1714,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* restricted range */
prop = RNA_def_property(srna, "use_restricted_range", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_RANGERESTRICT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Restrict Frame Range",
@@ -1678,6 +1724,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "sfra");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifier_start_frame_set", "rna_FModifier_start_frame_range");
RNA_def_property_ui_text(
@@ -1688,6 +1735,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "efra");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifier_end_frame_set", "rna_FModifier_end_frame_range");
RNA_def_property_ui_text(
@@ -1698,6 +1746,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "blendin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range");
RNA_def_property_ui_text(
prop, "Blend In", "Number of frames from start frame for influence to take effect");
@@ -1705,6 +1754,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "blendout");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range");
RNA_def_property_ui_text(
prop, "Blend Out", "Number of frames from end frame for influence to fade out");
@@ -1713,12 +1763,14 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* influence */
prop = RNA_def_property(srna, "use_influence", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_USEINFLUENCE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use Influence", "F-Curve Modifier's effects will be tempered by a default factor");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "influence");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(
@@ -2162,6 +2214,7 @@ static void rna_def_fcurve_modifiers(BlenderRNA *brna, PropertyRNA *cprop)
/* Collection active property */
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "FModifier");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(
prop, "rna_FCurve_active_modifier_get", "rna_FCurve_active_modifier_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE);
@@ -2378,6 +2431,7 @@ static void rna_def_fcurve(BlenderRNA *brna)
prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCURVE_MUTED);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Muted", "Disable F-Curve Modifier evaluation");
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, "rna_FCurve_update_eval");
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 1a0497b72f4..19ed5f960cf 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -1037,7 +1037,7 @@ static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src)
static bGPDlayer *rna_GPencil_layer_new(bGPdata *gpd, const char *name, bool setactive)
{
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0, false);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -1625,7 +1625,7 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
/* Material Index */
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "mat_nr");
- RNA_def_property_ui_text(prop, "Material Index", "Index of material used in this stroke");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this stroke");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
/* Settings */
@@ -2114,6 +2114,12 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
"ViewLayer",
"Only include Layer in this View Layer render output (leave blank to include always)");
+ prop = RNA_def_property(srna, "use_viewlayer_masks", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER);
+ RNA_def_property_ui_text(
+ prop, "Use Masks in Render", "Include the mask layers when rendering the viewlayer");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
/* blend mode */
prop = RNA_def_property(srna, "blend_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "blend_mode");
@@ -2197,7 +2203,10 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_mask_layer", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_USE_MASK);
- RNA_def_property_ui_text(prop, "Mask Layer", "Mask pixels from underlying layers drawing");
+ RNA_def_property_ui_text(
+ prop,
+ "Use Mask",
+ "The visibility of drawings on this layer is affected by the layers in its masks list");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "use_lights", PROP_BOOLEAN, PROP_NONE);
@@ -2568,7 +2577,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Auto-Lock Layers",
- "Lock automatically all layers except active one to avoid accidental changes");
+ "Automatically lock all layers except the active one to avoid accidental changes");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_autolock");
prop = RNA_def_property(srna, "edit_line_color", PROP_FLOAT, PROP_COLOR_GAMMA);
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 0aa2e652d2d..92d65961743 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -68,6 +68,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_BUILD,
"Build",
"Create duplication of strokes"},
+ {eGpencilModifierType_Lineart,
+ "GP_LINEART",
+ ICON_MOD_EDGESPLIT, /* TODO: Use a proper icon. */
+ "Line Art",
+ "Generate line art strokes from selected source"},
{eGpencilModifierType_Mirror,
"GP_MIRROR",
ICON_MOD_MIRROR,
@@ -88,11 +93,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_SUBSURF,
"Subdivide",
"Subdivide stroke adding more control points"},
- {eGpencilModifierType_Lineart,
- "GP_LINEART",
- ICON_MOD_EDGESPLIT, /* TODO: Use a proper icon. */
- "Line Art",
- "Generate line art strokes from selected source"},
{0, "", 0, N_("Deform"), ""},
{eGpencilModifierType_Armature,
"GP_ARMATURE",
@@ -109,6 +109,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_LATTICE,
"Lattice",
"Deform strokes using lattice"},
+ {eGpencilModifierType_Length,
+ "GP_LENGTH",
+ ICON_MOD_EDGESPLIT,
+ "Length",
+ "Extend or shrink strokes"},
{eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"},
{eGpencilModifierType_Offset,
"GP_OFFSET",
@@ -188,12 +193,18 @@ static const EnumPropertyItem gpencil_tint_type_items[] = {
{GP_TINT_GRADIENT, "GRADIENT", 0, "Gradient", ""},
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem gpencil_length_mode_items[] = {
+ {GP_LENGTH_RELATIVE, "RELATIVE", 0, "Relative", "Length in ratio to the stroke's length"},
+ {GP_LENGTH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Length in geometry space"},
+ {0, NULL, 0, NULL, NULL},
+};
#endif
#ifdef RNA_RUNTIME
# include "DNA_curve_types.h"
# include "DNA_fluid_types.h"
+# include "DNA_material_types.h"
# include "DNA_particle_types.h"
# include "BKE_cachefile.h"
@@ -232,6 +243,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr)
return &RNA_OpacityGpencilModifier;
case eGpencilModifierType_Lattice:
return &RNA_LatticeGpencilModifier;
+ case eGpencilModifierType_Length:
+ return &RNA_LengthGpencilModifier;
case eGpencilModifierType_Mirror:
return &RNA_MirrorGpencilModifier;
case eGpencilModifierType_Smooth:
@@ -350,6 +363,8 @@ static void greasepencil_modifier_object_set(Object *self,
RNA_GP_MOD_OBJECT_SET(Armature, object, OB_ARMATURE);
RNA_GP_MOD_OBJECT_SET(Lattice, object, OB_LATTICE);
RNA_GP_MOD_OBJECT_SET(Mirror, object, OB_EMPTY);
+RNA_GP_MOD_OBJECT_SET(Opacity, object, OB_EMPTY);
+RNA_GP_MOD_OBJECT_SET(Thick, object, OB_EMPTY);
# undef RNA_GP_MOD_OBJECT_SET
@@ -434,6 +449,195 @@ static void rna_GpencilModifier_opacity_update(Main *bmain, Scene *scene, Pointe
rna_GpencilModifier_update(bmain, scene, ptr);
}
+bool rna_GpencilModifier_material_poll(PointerRNA *ptr, PointerRNA value)
+{
+ Object *ob = (Object *)ptr->owner_id;
+ Material *ma = (Material *)value.owner_id;
+
+ return BKE_gpencil_object_material_index_get(ob, ma) != -1;
+}
+
+static void rna_GpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ Material **ma_target,
+ struct ReportList *reports)
+{
+ Object *ob = (Object *)ptr->owner_id;
+ Material *ma = (Material *)value.owner_id;
+
+ if (ma == NULL || BKE_gpencil_object_material_index_get(ob, ma) != -1) {
+ id_lib_extern((ID *)ob);
+ *ma_target = ma;
+ }
+ else {
+ BKE_reportf(
+ reports,
+ RPT_ERROR,
+ "Cannot assign material '%s', it has to be used by the grease pencil object already",
+ ma->id.name);
+ }
+}
+
+static void rna_LineartGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)ptr->data;
+ Material **ma_target = &lmd->target_material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_NoiseGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ NoiseGpencilModifierData *nmd = (NoiseGpencilModifierData *)ptr->data;
+ Material **ma_target = &nmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_SmoothGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ SmoothGpencilModifierData *smd = (SmoothGpencilModifierData *)ptr->data;
+ Material **ma_target = &smd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_SubdivGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ SubdivGpencilModifierData *smd = (SubdivGpencilModifierData *)ptr->data;
+ Material **ma_target = &smd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_SimplifyGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ SimplifyGpencilModifierData *smd = (SimplifyGpencilModifierData *)ptr->data;
+ Material **ma_target = &smd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_ThickGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ ThickGpencilModifierData *tmd = (ThickGpencilModifierData *)ptr->data;
+ Material **ma_target = &tmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_OffsetGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ OffsetGpencilModifierData *omd = (OffsetGpencilModifierData *)ptr->data;
+ Material **ma_target = &omd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_TintGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ TintGpencilModifierData *tmd = (TintGpencilModifierData *)ptr->data;
+ Material **ma_target = &tmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_ColorGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ ColorGpencilModifierData *cmd = (ColorGpencilModifierData *)ptr->data;
+ Material **ma_target = &cmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_ArrayGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ ArrayGpencilModifierData *amd = (ArrayGpencilModifierData *)ptr->data;
+ Material **ma_target = &amd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_OpacityGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ OpacityGpencilModifierData *omd = (OpacityGpencilModifierData *)ptr->data;
+ Material **ma_target = &omd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_LatticeGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ LatticeGpencilModifierData *lmd = (LatticeGpencilModifierData *)ptr->data;
+ Material **ma_target = &lmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_MirrorGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)ptr->data;
+ Material **ma_target = &mmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_HookGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ HookGpencilModifierData *hmd = (HookGpencilModifierData *)ptr->data;
+ Material **ma_target = &hmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_MultiplyGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)ptr->data;
+ Material **ma_target = &mmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_TextureGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ TextureGpencilModifierData *tmd = (TextureGpencilModifierData *)ptr->data;
+ Material **ma_target = &tmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
#else
static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
@@ -446,14 +650,20 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "NoiseGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_NOISE);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_NoiseGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -567,6 +777,8 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_INVERT_LAYERPASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna)
@@ -579,14 +791,20 @@ static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SmoothGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_SMOOTH);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_SmoothGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -680,6 +898,8 @@ static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna)
@@ -692,14 +912,20 @@ static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SubdivGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_SUBSURF);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_SubdivGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -746,6 +972,8 @@ static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SUBDIV_INVERT_LAYERPASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
@@ -782,14 +1010,20 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SimplifyGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_SIMPLIFY);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_SimplifyGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -859,6 +1093,8 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 1.0, 0.01, 3);
RNA_def_property_ui_text(prop, "Distance", "Distance between points");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
@@ -871,14 +1107,20 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ThickGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_THICKNESS);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_ThickGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -901,6 +1143,37 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Thickness Factor", "Factor to multiply the thickness with");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_FADING);
+ RNA_def_property_ui_text(prop, "Fading", "Fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ /* Distance reference object */
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Object", "Object used as distance reference");
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_ThickGpencilModifier_object_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_start");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end_factor");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3);
+ RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "pass_index");
RNA_def_property_range(prop, 0, 100);
@@ -953,6 +1226,8 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_thickness");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpenciloffset(BlenderRNA *brna)
@@ -965,14 +1240,20 @@ static void rna_def_modifier_gpenciloffset(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "OffsetGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_OFFSET);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_OffsetGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1036,6 +1317,36 @@ static void rna_def_modifier_gpenciloffset(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Scale", "Values for changes in scale");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_offset", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_offset");
+ RNA_def_property_ui_text(prop, "Random Offset", "Value for changes in location");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_EULER);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_rot");
+ RNA_def_property_ui_text(prop, "Random Rotation", "Value for changes in rotation");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_scale", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_scale");
+ RNA_def_property_ui_text(prop, "Scale", "Value for changes in scale");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(prop, "Seed", "Random seed");
+ 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_OFFSET_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");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
@@ -1056,10 +1367,11 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "TintGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_COLOR);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Parent object to define the center of the effect");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(prop, NULL, "rna_TintGpencilModifier_object_set", NULL, NULL);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
@@ -1069,8 +1381,12 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_TintGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1171,6 +1487,8 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpenciltime(BlenderRNA *brna)
@@ -1183,6 +1501,8 @@ static void rna_def_modifier_gpenciltime(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "TimeGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_TIME);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
RNA_def_property_enum_items(prop, rna_enum_time_mode_items);
@@ -1250,6 +1570,8 @@ static void rna_def_modifier_gpenciltime(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Custom Range", "Define a custom range of frames to use in modifier");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilcolor(BlenderRNA *brna)
@@ -1262,6 +1584,8 @@ static void rna_def_modifier_gpencilcolor(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ColorGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_TINT);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "modify_color", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_modify_color_items); /* share the enum */
RNA_def_property_ui_text(prop, "Mode", "Set what colors of the stroke are affected");
@@ -1273,8 +1597,12 @@ static void rna_def_modifier_gpencilcolor(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_ColorGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1341,6 +1669,8 @@ static void rna_def_modifier_gpencilcolor(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
@@ -1353,6 +1683,8 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "OpacityGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_OPACITY);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "modify_color", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_modify_opacity_items);
RNA_def_property_ui_text(prop, "Mode", "Set what colors of the stroke are affected");
@@ -1364,8 +1696,12 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_OpacityGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1390,6 +1726,37 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Hardness", "Factor of stroke hardness");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_FADING);
+ RNA_def_property_ui_text(prop, "Fading", "Fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ /* Distance reference object */
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Object", "Object used as distance reference");
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_OpacityGpencilModifier_object_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_start");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end_factor");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3);
+ RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "pass_index");
RNA_def_property_range(prop, 0, 100);
@@ -1442,6 +1809,8 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
@@ -1454,14 +1823,20 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ArrayGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_ARRAY);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_ArrayGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1486,7 +1861,6 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
"Use the location and rotation of another object to determine the distance and "
"rotational change between arrayed items");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "constant_offset", PROP_FLOAT, PROP_TRANSLATION);
@@ -1581,6 +1955,8 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
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");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilbuild(BlenderRNA *brna)
@@ -1643,6 +2019,8 @@ static void rna_def_modifier_gpencilbuild(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "BuildGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_BUILD);
+ RNA_define_lib_overridable(true);
+
/* Mode */
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_gpencil_build_mode_items);
@@ -1740,6 +2118,8 @@ static void rna_def_modifier_gpencilbuild(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BUILD_INVERT_LAYERPASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencillattice(BlenderRNA *brna)
@@ -1753,14 +2133,20 @@ static void rna_def_modifier_gpencillattice(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "LatticeGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_LATTICE);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_LatticeGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1812,7 +2198,6 @@ static void rna_def_modifier_gpencillattice(BlenderRNA *brna)
RNA_def_property_pointer_funcs(
prop, NULL, "rna_LatticeGpencilModifier_object_set", NULL, "rna_Lattice_object_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
@@ -1820,6 +2205,8 @@ static void rna_def_modifier_gpencillattice(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 1, 10, 2);
RNA_def_property_ui_text(prop, "Strength", "Strength of modifier effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
@@ -1832,14 +2219,20 @@ static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "MirrorGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_MIRROR);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_MirrorGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1879,7 +2272,6 @@ static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Object", "Object used as center");
RNA_def_property_pointer_funcs(prop, NULL, "rna_MirrorGpencilModifier_object_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "use_clip", PROP_BOOLEAN, PROP_NONE);
@@ -1901,6 +2293,8 @@ static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_AXIS_Z);
RNA_def_property_ui_text(prop, "Z", "Mirror the Z axis");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
@@ -1914,11 +2308,12 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "HookGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_HOOK);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(
prop, "Object", "Parent Object for hook, also recalculates and clears offset");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(prop, NULL, "rna_HookGpencilModifier_object_set", NULL, NULL);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
@@ -1936,8 +2331,12 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_HookGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -2025,6 +2424,8 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_HOOK_UNIFORM_SPACE);
RNA_def_property_ui_text(prop, "Uniform Falloff", "Compensate for non-uniform object scale");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilarmature(BlenderRNA *brna)
@@ -2038,12 +2439,13 @@ static void rna_def_modifier_gpencilarmature(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ArmatureGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_ARMATURE);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Armature object to deform with");
RNA_def_property_pointer_funcs(
prop, NULL, "rna_ArmatureGpencilModifier_object_set", NULL, "rna_Armature_object_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "use_bone_envelopes", PROP_BOOLEAN, PROP_NONE);
@@ -2075,6 +2477,8 @@ static void rna_def_modifier_gpencilarmature(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "deformflag", ARM_DEF_INVERT_VGROUP);
RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
@@ -2087,14 +2491,20 @@ static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "MultiplyGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_GP_MULTIFRAME_EDITING);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_MultiplyGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -2167,6 +2577,8 @@ static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
RNA_def_property_range(prop, 0, 1);
RNA_def_property_ui_text(prop, "Center", "Fade center");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
@@ -2205,6 +2617,8 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "TextureGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_TEXTURE);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
@@ -2216,8 +2630,12 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_TextureGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -2311,6 +2729,8 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_ui_text(prop, "Mode", "");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
@@ -2331,6 +2751,8 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "LineartGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR);
RNA_def_property_ui_text(prop,
@@ -2407,7 +2829,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop = RNA_def_property(srna, "source_object", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Object");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Source Object", "Source object that this modifier uses data from");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
@@ -2415,7 +2836,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop = RNA_def_property(srna, "source_collection", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Collection");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Source Collection", "Source collection that this modifier uses data from");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
@@ -2466,9 +2886,13 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "target_material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "Material");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_LineartGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(
prop, "Target Material", "Grease Pencil material assigned to the generated strokes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -2501,12 +2925,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Match Output", "Match output vertex group based on name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "use_soft_selection", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_SOFT_SELECTION);
- RNA_def_property_ui_text(
- prop, "Clip", "Preserve original vertex weight instead of clipping to 0/1");
- RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
-
prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_IS_BAKED);
RNA_def_property_ui_text(prop, "Is Baked", "This modifier has baked data");
@@ -2524,15 +2942,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "resample_length", PROP_FLOAT, PROP_DISTANCE);
- RNA_def_property_ui_text(prop,
- "Resample Length",
- "Resample the strokes so that the stroke points have the specified "
- "length between them. Zero length disables the resampling");
- RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01f, 2);
- RNA_def_property_range(prop, 0.0f, 1.0f);
- RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
-
prop = RNA_def_property(srna, "use_transparency", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_flags", LRT_GPENCIL_TRANSPARENCY_ENABLE);
RNA_def_property_ui_text(
@@ -2550,6 +2959,92 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_array(prop, 8);
RNA_def_property_ui_text(prop, "Mask", "");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
+}
+
+static void rna_def_modifier_gpencillength(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "LengthGpencilModifier", "GpencilModifier");
+ RNA_def_struct_ui_text(srna, "Length Modifier", "Stretch or shrink strokes");
+ RNA_def_struct_sdna(srna, "LengthGpencilModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
+
+ RNA_define_lib_overridable(true);
+
+ prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "start_fac");
+ RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1);
+ RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "end_fac");
+ RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1);
+ RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "overshoot_factor", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "overshoot_fac");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(
+ prop,
+ "Overshoot Factor",
+ "Defines how precise must follow the stroke trajectory for the overshoot extremes");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "mode");
+ RNA_def_property_enum_items(prop, gpencil_length_mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Mode to define length");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "layername");
+ RNA_def_property_ui_text(prop, "Layer", "Layer name");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "pass_index");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYER);
+ RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_MATERIAL);
+ RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_PASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "layer_pass");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Layer pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYERPASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
@@ -2627,6 +3122,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
rna_def_modifier_gpencilmultiply(brna);
rna_def_modifier_gpenciltexture(brna);
rna_def_modifier_gpencillineart(brna);
+ rna_def_modifier_gpencillength(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 95972dd444f..bfe9d4bb77c 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -476,7 +476,7 @@ void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
#endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
-#ifdef WITH_GEOMETRY_NODES
+#ifdef WITH_SIMULATION_DATABLOCK
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop);
#endif
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index 0c0260c889c..245730919b0 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -400,6 +400,7 @@ typedef struct IntPropertyRNA {
PropIntArraySetFuncEx setarray_ex;
PropIntRangeFuncEx range_ex;
+ PropertyScaleType ui_scale_type;
int softmin, softmax;
int hardmin, hardmax;
int step;
@@ -423,6 +424,7 @@ typedef struct FloatPropertyRNA {
PropFloatArraySetFuncEx setarray_ex;
PropFloatRangeFuncEx range_ex;
+ PropertyScaleType ui_scale_type;
float softmin, softmax;
float hardmin, hardmax;
float step;
diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c
index 3b9fc970072..a48727526c9 100644
--- a/source/blender/makesrna/intern/rna_key.c
+++ b/source/blender/makesrna/intern/rna_key.c
@@ -699,6 +699,16 @@ static void rna_Key_update_data(Main *bmain, Scene *UNUSED(scene), PointerRNA *p
}
}
+static void rna_ShapeKey_update_minmax(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ KeyBlock *data = (KeyBlock *)ptr->data;
+ if (IN_RANGE_INCL(data->curval, data->slidermin, data->slidermax)) {
+ return;
+ }
+ CLAMP(data->curval, data->slidermin, data->slidermax);
+ rna_Key_update_data(bmain, scene, ptr);
+}
+
static KeyBlock *rna_ShapeKeyData_find_keyblock(Key *key, float *point)
{
KeyBlock *kb;
@@ -955,6 +965,7 @@ static void rna_def_keyblock(BlenderRNA *brna)
RNA_def_property_float_funcs(
prop, NULL, "rna_ShapeKey_slider_min_set", "rna_ShapeKey_slider_min_range");
RNA_def_property_ui_text(prop, "Slider Min", "Minimum for slider");
+ RNA_def_property_update(prop, 0, "rna_ShapeKey_update_minmax");
prop = RNA_def_property(srna, "slider_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "slidermax");
@@ -963,6 +974,7 @@ static void rna_def_keyblock(BlenderRNA *brna)
RNA_def_property_float_funcs(
prop, NULL, "rna_ShapeKey_slider_max_set", "rna_ShapeKey_slider_max_range");
RNA_def_property_ui_text(prop, "Slider Max", "Maximum for slider");
+ RNA_def_property_update(prop, 0, "rna_ShapeKey_update_minmax");
prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "data", "totelem");
diff --git a/source/blender/makesrna/intern/rna_lattice_api.c b/source/blender/makesrna/intern/rna_lattice_api.c
index 0b61603dd09..5b69a743d47 100644
--- a/source/blender/makesrna/intern/rna_lattice_api.c
+++ b/source/blender/makesrna/intern/rna_lattice_api.c
@@ -33,7 +33,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Lattice_transform(Lattice *lt, float *mat, bool shape_keys)
+static void rna_Lattice_transform(Lattice *lt, float mat[16], bool shape_keys)
{
BKE_lattice_transform(lt, (float(*)[4])mat, shape_keys);
diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c
index bb99e5c6c1d..0593db0dd56 100644
--- a/source/blender/makesrna/intern/rna_light.c
+++ b/source/blender/makesrna/intern/rna_light.c
@@ -480,6 +480,15 @@ static void rna_def_area_light(BlenderRNA *brna)
"Size Y",
"Size of the area of the area light in the Y direction for rectangle shapes");
RNA_def_property_update(prop, 0, "rna_Light_draw_update");
+
+ prop = RNA_def_property(srna, "spread", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "area_spread");
+ RNA_def_property_range(prop, DEG2RADF(1.0f), DEG2RADF(180.0f));
+ RNA_def_property_ui_text(
+ prop,
+ "Spread",
+ "How widely the emitted light fans out, as in the case of a gridded softbox");
+ RNA_def_property_update(prop, 0, "rna_Light_draw_update");
}
static void rna_def_spot_light(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index c80f856dd6b..464abc6b543 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -134,7 +134,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds)
RNA_MAIN_LISTBASE_FUNCS_DEF(scenes)
RNA_MAIN_LISTBASE_FUNCS_DEF(screens)
RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys)
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
RNA_MAIN_LISTBASE_FUNCS_DEF(simulations)
# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(sounds)
@@ -407,7 +407,7 @@ void RNA_def_main(BlenderRNA *brna)
"Volumes",
"Volume data-blocks",
RNA_def_main_volumes},
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
{"simulations",
"Simulation",
"rna_Main_simulations_begin",
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index d24be91f731..8e6ff961721 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -806,7 +806,7 @@ static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
return volume;
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
static Simulation *rna_Main_simulations_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -870,7 +870,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA)
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
+# ifdef WITH_SIMULATION_DATABLOCK
RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM)
# endif
@@ -996,7 +996,7 @@ void RNA_def_main_objects(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
- RNA_def_function_ui_description(func, "Remove a object from the current blendfile");
+ RNA_def_function_ui_description(func, "Remove an object from the current blendfile");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "object", "Object", "", "Object to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
@@ -1200,7 +1200,7 @@ void RNA_def_main_lights(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_string(func, "name", "Light", 0, "", "New name for the data-block");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_enum(
- func, "type", rna_enum_light_type_items, 0, "Type", "The type of texture to add");
+ func, "type", rna_enum_light_type_items, 0, "Type", "The type of light to add");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return type */
parm = RNA_def_pointer(func, "light", "Light", "", "New light data-block");
@@ -1216,7 +1216,7 @@ void RNA_def_main_lights(BlenderRNA *brna, PropertyRNA *cprop)
"do_unlink",
true,
"",
- "Unlink all usages of this Light before deleting it "
+ "Unlink all usages of this light before deleting it "
"(WARNING: will also delete objects instancing that light data)");
RNA_def_boolean(func,
"do_id_user",
@@ -1248,7 +1248,7 @@ void RNA_def_main_libraries(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a camera from the current blendfile");
+ RNA_def_function_ui_description(func, "Remove a library from the current blendfile");
parm = RNA_def_pointer(func, "library", "Library", "", "Library to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
@@ -1258,9 +1258,9 @@ void RNA_def_main_libraries(BlenderRNA *brna, PropertyRNA *cprop)
"do_id_user",
true,
"",
- "Decrement user counter of all datablocks used by this object");
+ "Decrement user counter of all datablocks used by this library");
RNA_def_boolean(
- func, "do_ui_user", true, "", "Make sure interface does not reference this object");
+ func, "do_ui_user", true, "", "Make sure interface does not reference this library");
}
void RNA_def_main_screens(BlenderRNA *brna, PropertyRNA *cprop)
@@ -1327,7 +1327,7 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Load a new image into the main database");
parm = RNA_def_string_file_path(
- func, "filepath", "File Path", 0, "", "path of the file to load");
+ func, "filepath", "File Path", 0, "", "Path of the file to load");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_boolean(func,
"check_existing",
@@ -1372,7 +1372,7 @@ void RNA_def_main_lattices(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_string(func, "name", "Lattice", 0, "", "New name for the data-block");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return type */
- parm = RNA_def_pointer(func, "lattice", "Lattice", "", "New lattices data-block");
+ parm = RNA_def_pointer(func, "lattice", "Lattice", "", "New lattice data-block");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
@@ -1857,7 +1857,7 @@ void RNA_def_main_armatures(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a armature from the current blendfile");
+ RNA_def_function_ui_description(func, "Remove an armature from the current blendfile");
parm = RNA_def_pointer(func, "armature", "Armature", "", "Armature to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
@@ -1900,7 +1900,7 @@ void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a action from the current blendfile");
+ RNA_def_function_ui_description(func, "Remove an action from the current blendfile");
parm = RNA_def_pointer(func, "action", "Action", "", "Action to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
@@ -2158,7 +2158,7 @@ void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop)
/* remove func */
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a masks from the current blendfile.");
+ RNA_def_function_ui_description(func, "Remove a mask from the current blendfile");
parm = RNA_def_pointer(func, "mask", "Mask", "", "Mask to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
@@ -2238,11 +2238,11 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_struct_ui_text(srna, "Main Light Probes", "Collection of light probes");
func = RNA_def_function(srna, "new", "rna_Main_lightprobe_new");
- RNA_def_function_ui_description(func, "Add a new probe to the main database");
+ RNA_def_function_ui_description(func, "Add a new light probe to the main database");
parm = RNA_def_string(func, "name", "Probe", 0, "", "New name for the data-block");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_enum(
- func, "type", rna_enum_lightprobes_type_items, 0, "Type", "The type of lightprobe to add");
+ func, "type", rna_enum_lightprobes_type_items, 0, "Type", "The type of light probe to add");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return type */
parm = RNA_def_pointer(func, "lightprobe", "LightProbe", "", "New light probe data-block");
@@ -2250,15 +2250,15 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a probe from the current blendfile");
- parm = RNA_def_pointer(func, "lightprobe", "LightProbe", "", "Probe to remove");
+ RNA_def_function_ui_description(func, "Remove a light probe from the current blendfile");
+ parm = RNA_def_pointer(func, "lightprobe", "LightProbe", "", "Light probe to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
RNA_def_boolean(func,
"do_unlink",
true,
"",
- "Unlink all usages of this probe before deleting it "
+ "Unlink all usages of this light probe before deleting it "
"(WARNING: will also delete objects instancing that light probe data)");
RNA_def_boolean(func,
"do_id_user",
@@ -2412,7 +2412,7 @@ void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 7ef1904fc34..a4052430d9a 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -840,6 +840,8 @@ void RNA_def_material(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ /* XXX: remove once overrides in material node trees are supported. */
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based materials");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index cf7d1f30dde..d5a1047d287 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -958,13 +958,14 @@ static void rna_MeshPaintMaskLayer_data_begin(CollectionPropertyIterator *iter,
{
Mesh *me = rna_mesh(ptr);
CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
- rna_iterator_array_begin(iter, layer->data, sizeof(MFloatProperty), me->totvert, 0, NULL);
+ rna_iterator_array_begin(
+ iter, layer->data, sizeof(MFloatProperty), (me->edit_mesh) ? 0 : me->totvert, 0, NULL);
}
static int rna_MeshPaintMaskLayer_data_length(PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
- return me->totvert;
+ return (me->edit_mesh) ? 0 : me->totvert;
}
/* End paint mask */
@@ -987,13 +988,14 @@ static void rna_MeshFaceMapLayer_data_begin(CollectionPropertyIterator *iter, Po
{
Mesh *me = rna_mesh(ptr);
CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
- rna_iterator_array_begin(iter, layer->data, sizeof(int), me->totpoly, 0, NULL);
+ rna_iterator_array_begin(
+ iter, layer->data, sizeof(int), (me->edit_mesh) ? 0 : me->totpoly, 0, NULL);
}
static int rna_MeshFaceMapLayer_data_length(PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
- return me->totpoly;
+ return (me->edit_mesh) ? 0 : me->totpoly;
}
static PointerRNA rna_Mesh_face_map_new(struct Mesh *me, ReportList *reports, const char *name)
@@ -1821,7 +1823,7 @@ static void rna_def_mlooptri(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_int_funcs(prop, "rna_MeshLoopTriangle_material_index_get", NULL, NULL);
- RNA_def_property_ui_text(prop, "Material Index", "");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this triangle");
prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -1931,7 +1933,7 @@ static void rna_def_mpolygon(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "mat_nr");
- RNA_def_property_ui_text(prop, "Material Index", "");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this polygon");
# if 0
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MeshPoly_material_index_range");
# endif
@@ -3289,10 +3291,12 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Z", "Enable symmetry in the Z axis");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
- prop = RNA_def_property(srna, "use_mirror_vertex_group_x", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_VERTEX_GROUPS_X_SYMMETRY);
- RNA_def_property_ui_text(
- prop, "Vertex Groups X Symmetry", "Mirror the left/right vertex groups when painting");
+ prop = RNA_def_property(srna, "use_mirror_vertex_groups", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_MIRROR_VERTEX_GROUPS);
+ RNA_def_property_ui_text(prop,
+ "Mirror Vertex Groups",
+ "Mirror the left/right vertex groups when painting. The symmetry axis "
+ "is determined by the symmetry settings");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
/* End Symmetry */
diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c
index f92a2932f06..2b0582cae9a 100644
--- a/source/blender/makesrna/intern/rna_mesh_api.c
+++ b/source/blender/makesrna/intern/rna_mesh_api.c
@@ -172,7 +172,7 @@ static void rna_Mesh_normals_split_custom_set_from_vertices(Mesh *mesh,
DEG_id_tag_update(&mesh->id, 0);
}
-static void rna_Mesh_transform(Mesh *mesh, float *mat, bool shape_keys)
+static void rna_Mesh_transform(Mesh *mesh, float mat[16], bool shape_keys)
{
BKE_mesh_transform(mesh, (float(*)[4])mat, shape_keys);
diff --git a/source/blender/makesrna/intern/rna_meta_api.c b/source/blender/makesrna/intern/rna_meta_api.c
index 19d0b35959b..93bd9fe3b9c 100644
--- a/source/blender/makesrna/intern/rna_meta_api.c
+++ b/source/blender/makesrna/intern/rna_meta_api.c
@@ -35,7 +35,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Meta_transform(struct MetaBall *mb, float *mat)
+static void rna_Meta_transform(struct MetaBall *mb, float mat[16])
{
BKE_mball_transform(mb, (float(*)[4])mat, true);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 98a2b683f18..674e5845ccb 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -2226,6 +2226,14 @@ static void rna_def_modifier_mirror(BlenderRNA *brna)
prop, "Merge Distance", "Distance within which mirrored vertices are merged");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "bisect_threshold", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_sdna(prop, NULL, "bisect_threshold");
+ RNA_def_property_range(prop, 0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0, 1, 0.01, 6);
+ RNA_def_property_ui_text(
+ prop, "Bisect Distance", "Distance from the bisect plane within which vertices are removed");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "mirror_object", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "mirror_ob");
RNA_def_property_ui_text(prop, "Mirror Object", "Object to use as mirror");
@@ -2773,7 +2781,8 @@ static void rna_def_modifier_boolean(BlenderRNA *brna)
prop = RNA_def_property(srna, "double_threshold", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "double_threshold");
RNA_def_property_range(prop, 0, 1.0f);
- RNA_def_property_ui_range(prop, 0, 1, 0.0001, 6);
+ RNA_def_property_ui_range(prop, 0, 1, 1.0, 6);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_LOG);
RNA_def_property_ui_text(
prop, "Overlap Threshold", "Threshold for checking overlapping geometry");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -5522,6 +5531,7 @@ static void rna_def_modifier_remesh(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "voxel_size");
RNA_def_property_range(prop, 0.0001f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0001, 2, 0.1, 3);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_LOG);
RNA_def_property_ui_text(prop,
"Voxel Size",
"Size of the voxel in object space used for volume evaluation. Lower "
@@ -5920,8 +5930,9 @@ static void rna_def_modifier_triangulate(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Keep Normals",
- "Try to preserve custom normals (WARNING: depending on chosen triangulation method, "
- "shading may not be fully preserved, 'Fixed' method usually gives the best result here)");
+ "Try to preserve custom normals.\n"
+ "Warning: Depending on chosen triangulation method, "
+ "shading may not be fully preserved, \"Fixed\" method usually gives the best result here");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c
index c9520c939f4..f8fa2aab5e7 100644
--- a/source/blender/makesrna/intern/rna_movieclip.c
+++ b/source/blender/makesrna/intern/rna_movieclip.c
@@ -402,7 +402,7 @@ static void rna_def_movieclip(BlenderRNA *brna)
"Start Frame",
"Global scene frame number at which this movie starts playing "
"(affects all data associated with a clip)");
- RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
/* frame_offset */
prop = RNA_def_property(srna, "frame_offset", PROP_INT, PROP_NONE);
@@ -412,7 +412,7 @@ static void rna_def_movieclip(BlenderRNA *brna)
"Frame Offset",
"Offset of footage first frame relative to its file name "
"(affects only how footage is loading, does not change data associated with a clip)");
- RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
/* length */
prop = RNA_def_property(srna, "frame_duration", PROP_INT, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index d69a1e3dd06..2a1ea3d6716 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -65,6 +65,22 @@
const EnumPropertyItem rna_enum_node_socket_in_out_items[] = {
{SOCK_IN, "IN", 0, "Input", ""}, {SOCK_OUT, "OUT", 0, "Output", ""}, {0, NULL, 0, NULL, NULL}};
+static const EnumPropertyItem node_socket_data_type_items[] = {
+ {SOCK_FLOAT, "FLOAT", 0, "Float", ""},
+ {SOCK_INT, "INT", 0, "Integer", ""},
+ {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
+ {SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
+ {SOCK_STRING, "STRING", 0, "String", ""},
+ {SOCK_RGBA, "RGBA", 0, "Color", ""},
+ {SOCK_OBJECT, "OBJECT", 0, "Object", ""},
+ {SOCK_IMAGE, "IMAGE", 0, "Image", ""},
+ {SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
+ {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
+ {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
+ {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifndef RNA_RUNTIME
static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
{SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""},
@@ -78,7 +94,7 @@ static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_CUSTOM, "CUSTOM", 0, "Custom", ""},
{SOCK_FLOAT, "VALUE", 0, "Value", ""},
- {SOCK_INT, "INT", 0, "Int", ""},
+ {SOCK_INT, "INT", 0, "Integer", ""},
{SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_STRING, "STRING", 0, "String", ""},
@@ -88,6 +104,8 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
+ {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
+ {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -109,6 +127,20 @@ static const EnumPropertyItem node_chunksize_items[] = {
};
#endif
+static const EnumPropertyItem rna_enum_execution_mode_items[] = {
+ {NTREE_EXECUTION_MODE_TILED,
+ "TILED",
+ 0,
+ "Tiled",
+ "Compositing is tiled, having as priority to display first tiles as fast as possible"},
+ {NTREE_EXECUTION_MODE_FULL_FRAME,
+ "FULL_FRAME",
+ 0,
+ "Full Frame",
+ "Composites full image result as fast as possible"},
+ {0, NULL, 0, NULL, NULL},
+};
+
const EnumPropertyItem rna_enum_mapping_type_items[] = {
{NODE_MAPPING_TYPE_POINT, "POINT", 0, "Point", "Transform a point"},
{NODE_MAPPING_TYPE_TEXTURE,
@@ -216,6 +248,7 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = {
{NODE_VECTOR_MATH_SUBTRACT, "SUBTRACT", 0, "Subtract", "A - B"},
{NODE_VECTOR_MATH_MULTIPLY, "MULTIPLY", 0, "Multiply", "Entry-wise multiply"},
{NODE_VECTOR_MATH_DIVIDE, "DIVIDE", 0, "Divide", "Entry-wise divide"},
+ {NODE_VECTOR_MATH_MULTIPLY_ADD, "MULTIPLY_ADD", 0, "Multiply Add", "A * B + C"},
{0, "", ICON_NONE, NULL, NULL},
{NODE_VECTOR_MATH_CROSS_PRODUCT, "CROSS_PRODUCT", 0, "Cross Product", "A cross B"},
{NODE_VECTOR_MATH_PROJECT, "PROJECT", 0, "Project", "Project A onto B"},
@@ -329,8 +362,12 @@ const EnumPropertyItem rna_enum_node_map_range_items[] = {
};
const EnumPropertyItem rna_enum_node_clamp_items[] = {
- {NODE_CLAMP_MINMAX, "MINMAX", 0, "Min Max", "Clamp values using Min and Max values"},
- {NODE_CLAMP_RANGE, "RANGE", 0, "Range", "Clamp values between Min and Max range"},
+ {NODE_CLAMP_MINMAX, "MINMAX", 0, "Min Max", "Constrain value between min and max"},
+ {NODE_CLAMP_RANGE,
+ "RANGE",
+ 0,
+ "Range",
+ "Constrain value between min and max, swapping arguments when min > max"},
{0, NULL, 0, NULL, NULL},
};
@@ -449,6 +486,12 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vecto
ITEM_VECTOR,
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float_vector[] = {
+ ITEM_ATTRIBUTE,
+ ITEM_FLOAT,
+ ITEM_VECTOR,
+ {0, NULL, 0, NULL, NULL},
+};
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
@@ -487,6 +530,7 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_bo
# include "NOD_common.h"
# include "NOD_composite.h"
+# include "NOD_geometry.h"
# include "NOD_shader.h"
# include "NOD_socket.h"
@@ -946,6 +990,32 @@ static void rna_NodeTree_get_from_context(
RNA_parameter_list_free(&list);
}
+static bool rna_NodeTree_valid_socket_type(eNodeSocketDatatype socket_type,
+ bNodeTreeType *ntreetype)
+{
+ extern FunctionRNA rna_NodeTree_valid_socket_type_func;
+
+ PointerRNA ptr;
+ ParameterList list;
+ FunctionRNA *func;
+ void *ret;
+ bool valid;
+
+ RNA_pointer_create(NULL, ntreetype->rna_ext.srna, NULL, &ptr); /* dummy */
+ func = &rna_NodeTree_valid_socket_type_func;
+
+ RNA_parameter_list_create(&list, &ptr, func);
+ RNA_parameter_set_lookup(&list, "type", &socket_type);
+ ntreetype->rna_ext.call(NULL, &ptr, func, &list);
+
+ RNA_parameter_get_lookup(&list, "valid", &ret);
+ valid = *(bool *)ret;
+
+ RNA_parameter_list_free(&list);
+
+ return valid;
+}
+
static void rna_NodeTree_unregister(Main *UNUSED(bmain), StructRNA *type)
{
bNodeTreeType *nt = RNA_struct_blender_type_get(type);
@@ -974,7 +1044,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain,
bNodeTreeType *nt, dummynt;
bNodeTree dummyntree;
PointerRNA dummyptr;
- int have_function[3];
+ int have_function[4];
/* setup dummy tree & tree type to store static properties in */
memset(&dummynt, 0, sizeof(bNodeTreeType));
@@ -1020,6 +1090,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain,
nt->poll = (have_function[0]) ? rna_NodeTree_poll : NULL;
nt->update = (have_function[1]) ? rna_NodeTree_update_reg : NULL;
nt->get_from_context = (have_function[2]) ? rna_NodeTree_get_from_context : NULL;
+ nt->valid_socket_type = (have_function[3]) ? rna_NodeTree_valid_socket_type : NULL;
ntreeTypeAdd(nt);
@@ -1074,13 +1145,25 @@ static bNode *rna_NodeTree_node_new(bNodeTree *ntree,
return NULL;
}
- if (ntype->poll && !ntype->poll(ntype, ntree)) {
- BKE_reportf(reports,
- RPT_ERROR,
- "Cannot add node of type %s to node tree '%s'",
- type,
- ntree->id.name + 2);
- return NULL;
+ const char *disabled_hint = NULL;
+ if (ntype->poll && !ntype->poll(ntype, ntree, &disabled_hint)) {
+ if (disabled_hint) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Cannot add node of type %s to node tree '%s'\n %s",
+ type,
+ ntree->id.name + 2,
+ disabled_hint);
+ return NULL;
+ }
+ else {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Cannot add node of type %s to node tree '%s'",
+ type,
+ ntree->id.name + 2);
+ return NULL;
+ }
}
node = nodeAddNode(C, ntree, type);
@@ -1200,6 +1283,13 @@ static bNodeLink *rna_NodeTree_link_new(bNodeTree *ntree,
if (nodeCountSocketLinks(ntree, tosock) + 1 > nodeSocketLinkLimit(tosock)) {
nodeRemSocketLinks(ntree, tosock);
}
+ if (tosock->flag & SOCK_MULTI_INPUT) {
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
+ if (link->fromsock == fromsock && link->tosock == tosock) {
+ nodeRemLink(ntree, link);
+ }
+ }
+ }
}
ret = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
@@ -1355,6 +1445,7 @@ static void rna_NodeTree_socket_remove(bNodeTree *ntree,
ntreeRemoveSocketInterface(ntree, sock);
ntreeUpdateTree(bmain, ntree);
+ DEG_id_tag_update(&ntree->id, 0);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
@@ -1520,7 +1611,7 @@ char *rna_Node_ImageUser_path(PointerRNA *ptr)
return NULL;
}
-static bool rna_Node_poll(bNodeType *ntype, bNodeTree *ntree)
+static bool rna_Node_poll(bNodeType *ntype, bNodeTree *ntree, const char **UNUSED(r_disabled_hint))
{
extern FunctionRNA rna_Node_poll_func;
@@ -1545,7 +1636,9 @@ static bool rna_Node_poll(bNodeType *ntype, bNodeTree *ntree)
return visible;
}
-static bool rna_Node_poll_instance(bNode *node, bNodeTree *ntree)
+static bool rna_Node_poll_instance(bNode *node,
+ bNodeTree *ntree,
+ const char **UNUSED(disabled_info))
{
extern FunctionRNA rna_Node_poll_instance_func;
@@ -1570,10 +1663,12 @@ static bool rna_Node_poll_instance(bNode *node, bNodeTree *ntree)
return visible;
}
-static bool rna_Node_poll_instance_default(bNode *node, bNodeTree *ntree)
+static bool rna_Node_poll_instance_default(bNode *node,
+ bNodeTree *ntree,
+ const char **disabled_info)
{
/* use the basic poll function */
- return rna_Node_poll(node->typeinfo, ntree);
+ return rna_Node_poll(node->typeinfo, ntree, disabled_info);
}
static void rna_Node_update_reg(bNodeTree *ntree, bNode *node)
@@ -1886,6 +1981,44 @@ static const EnumPropertyItem *itemf_function_check(
return item_array;
}
+static bool switch_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value,
+ SOCK_FLOAT,
+ SOCK_INT,
+ SOCK_BOOLEAN,
+ SOCK_VECTOR,
+ SOCK_STRING,
+ SOCK_RGBA,
+ SOCK_GEOMETRY,
+ SOCK_OBJECT,
+ SOCK_COLLECTION,
+ SOCK_TEXTURE,
+ SOCK_MATERIAL);
+}
+
+static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(node_socket_data_type_items, switch_type_supported);
+}
+
+static bool attribute_clamp_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeClamp_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_clamp_type_supported);
+}
+
static bool attribute_random_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL, CD_PROP_INT32);
@@ -1943,6 +2076,7 @@ static void rna_GeometryNodeAttributeRandomize_data_type_update(Main *bmain,
static bool attribute_convert_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value,
+ CD_AUTO_FROM_NAME,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
CD_PROP_FLOAT3,
@@ -1954,7 +2088,8 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeConvert_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_convert_type_supported);
+ return itemf_function_check(rna_enum_attribute_type_with_auto_items,
+ attribute_convert_type_supported);
}
static bool attribute_fill_type_supported(const EnumPropertyItem *item)
@@ -2065,6 +2200,28 @@ static void rna_GeometryNodeAttributeVectorMath_operation_update(Main *bmain,
rna_Node_socket_update(bmain, scene, ptr);
}
+static bool attribute_map_range_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeMapRange_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_map_range_type_supported);
+}
+
+static bool attribute_curve_map_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeCurveMap_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_curve_map_type_supported);
+}
+
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@@ -3001,7 +3158,7 @@ static void rna_NodeSocketStandard_draw(ID *id,
}
static void rna_NodeSocketStandard_draw_color(
- ID *id, bNodeSocket *sock, struct bContext *C, PointerRNA *nodeptr, float *r_color)
+ ID *id, bNodeSocket *sock, struct bContext *C, PointerRNA *nodeptr, float r_color[4])
{
PointerRNA ptr;
RNA_pointer_create(id, &RNA_NodeSocket, sock, &ptr);
@@ -3021,7 +3178,7 @@ static void rna_NodeSocketInterfaceStandard_draw(ID *id,
static void rna_NodeSocketInterfaceStandard_draw_color(ID *id,
bNodeSocket *sock,
struct bContext *C,
- float *r_color)
+ float r_color[4])
{
PointerRNA ptr;
RNA_pointer_create(id, &RNA_NodeSocket, sock, &ptr);
@@ -3179,18 +3336,20 @@ static PointerRNA rna_NodeInternal_output_template(StructRNA *srna, int index)
static bool rna_NodeInternal_poll(StructRNA *srna, bNodeTree *ntree)
{
bNodeType *ntype = RNA_struct_blender_type_get(srna);
- return ntype && (!ntype->poll || ntype->poll(ntype, ntree));
+ const char *disabled_hint;
+ return ntype && (!ntype->poll || ntype->poll(ntype, ntree, &disabled_hint));
}
static bool rna_NodeInternal_poll_instance(bNode *node, bNodeTree *ntree)
{
bNodeType *ntype = node->typeinfo;
+ const char *disabled_hint;
if (ntype->poll_instance) {
- return ntype->poll_instance(node, ntree);
+ return ntype->poll_instance(node, ntree, &disabled_hint);
}
else {
/* fall back to basic poll function */
- return !ntype->poll || ntype->poll(ntype, ntree);
+ return !ntype->poll || ntype->poll(ntype, ntree, &disabled_hint);
}
}
@@ -3256,6 +3415,35 @@ static StructRNA *rna_NodeCustomGroup_register(Main *bmain,
return nt->rna_ext.srna;
}
+static StructRNA *rna_GeometryNodeCustomGroup_register(Main *bmain,
+ ReportList *reports,
+ void *data,
+ const char *identifier,
+ StructValidateFunc validate,
+ StructCallbackFunc call,
+ StructFreeFunc free)
+{
+ bNodeType *nt = rna_Node_register_base(
+ bmain, reports, &RNA_GeometryNodeCustomGroup, data, identifier, validate, call, free);
+
+ if (!nt) {
+ return NULL;
+ }
+
+ nt->group_update_func = node_group_update;
+ nt->type = NODE_CUSTOM_GROUP;
+
+ register_node_type_geo_custom_group(nt);
+
+ nodeRegisterType(nt);
+
+ WM_main_add_notifier(NC_NODE | NA_EDITED, NULL);
+
+ return nt->rna_ext.srna;
+}
+
+void register_node_type_geo_custom_group(bNodeType *ntype);
+
static StructRNA *rna_ShaderNodeCustomGroup_register(Main *bmain,
ReportList *reports,
void *data,
@@ -3344,7 +3532,8 @@ static void rna_NodeGroup_node_tree_set(PointerRNA *ptr,
bNode *node = ptr->data;
bNodeTree *ngroup = value.data;
- if (nodeGroupPoll(ntree, ngroup)) {
+ const char *disabled_hint = NULL;
+ if (nodeGroupPoll(ntree, ngroup, &disabled_hint)) {
if (node->id) {
id_us_min(node->id);
}
@@ -3366,7 +3555,8 @@ static bool rna_NodeGroup_node_tree_poll(PointerRNA *ptr, const PointerRNA value
return false;
}
- return nodeGroupPoll(ntree, ngroup);
+ const char *disabled_hint = NULL;
+ return nodeGroupPoll(ntree, ngroup, &disabled_hint);
}
static StructRNA *rna_NodeGroup_interface_typef(PointerRNA *ptr)
@@ -3807,7 +3997,7 @@ static void rna_NodeCryptomatte_layer_name_set(PointerRNA *ptr, int new_value)
}
}
-static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UNUSED(C),
+static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
@@ -3818,7 +4008,7 @@ static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UN
EnumPropertyItem template = {0, "", 0, "", ""};
int totitem = 0;
- ntreeCompositCryptomatteUpdateLayerNames(node);
+ ntreeCompositCryptomatteUpdateLayerNames(CTX_data_scene(C), node);
int layer_index;
LISTBASE_FOREACH_INDEX (CryptomatteLayer *, layer, &storage->runtime.layers, layer_index) {
template.value = layer_index;
@@ -3910,7 +4100,7 @@ static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value)
static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr)
{
- ntreeCompositCryptomatteSyncFromAdd(ptr->data);
+ ntreeCompositCryptomatteSyncFromAdd(scene, ptr->data);
rna_Node_update(bmain, scene, ptr);
}
@@ -4290,6 +4480,13 @@ void rna_ShaderNodePointDensity_density_minmax(bNode *self,
RE_point_density_minmax(depsgraph, pd, r_min, r_max);
}
+bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
+{
+ /* Do not show grease pencil materials for now. */
+ Material *ma = (Material *)value.data;
+ return ma->gp_style == NULL;
+}
+
#else
static const EnumPropertyItem prop_image_layer_items[] = {
@@ -8634,6 +8831,41 @@ static void def_cmp_denoise(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_cmp_antialiasing(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAntiAliasingData", "storage");
+
+ prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "threshold");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3);
+ RNA_def_property_ui_text(
+ prop,
+ "Threshold",
+ "Threshold to detect edges (smaller threshold makes more sensitive detection)");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "contrast_limit", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "contrast_limit");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3);
+ RNA_def_property_ui_text(
+ prop,
+ "Contrast Limit",
+ "How much to eliminate spurious edges to avoid artifacts (the larger value makes less "
+ "active; the value 2.0, for example, means discard a detected edge if there is a "
+ "neighboring edge that has 2.0 times bigger contrast than the current one)");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "corner_rounding", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "corner_rounding");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Corner Rounding", "How much sharp corners will be rounded");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
/* -- Texture Nodes --------------------------------------------------------- */
static void def_tex_output(StructRNA *srna)
@@ -8722,7 +8954,7 @@ static void def_geo_boolean(StructRNA *srna)
RNA_def_property_enum_items(prop, rna_node_geometry_boolean_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_BOOLEAN_INTERSECT);
RNA_def_property_ui_text(prop, "Operation", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_triangulate(StructRNA *srna)
@@ -8834,9 +9066,9 @@ static void def_geo_attribute_convert(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeAttributeConvert", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_with_auto_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeConvert_type_itemf");
- RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_enum_default(prop, CD_AUTO_FROM_NAME);
RNA_def_property_ui_text(prop, "Data Type", "The data type to save the result attribute with");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
@@ -8915,6 +9147,26 @@ static void def_geo_attribute_vector_math(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_attribute_map_range(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeMapRange", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeMapRange_type_itemf");
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "interpolation_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "interpolation_type");
+ RNA_def_property_enum_items(prop, rna_enum_node_map_range_items);
+ RNA_def_property_ui_text(prop, "Interpolation Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
+}
+
static void def_geo_point_instance(StructRNA *srna)
{
static const EnumPropertyItem instance_type_items[] = {
@@ -8975,6 +9227,26 @@ static void def_geo_attribute_mix(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_attribute_clamp(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeClamp", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeClamp_type_itemf");
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_node_clamp_items);
+ RNA_def_property_enum_default(prop, NODE_CLAMP_MINMAX);
+ RNA_def_property_ui_text(prop, "Operation", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_attribute_attribute_compare(StructRNA *srna)
{
PropertyRNA *prop;
@@ -8988,12 +9260,12 @@ static void def_geo_attribute_attribute_compare(StructRNA *srna)
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_any);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean);
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_any);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean);
RNA_def_property_ui_text(prop, "Input Type B", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
@@ -9037,6 +9309,85 @@ static void def_geo_attribute_color_ramp(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_attribute_curve_map(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeCurveMap", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeCurveMap_type_itemf");
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "curve_vec", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Mapping", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "curve_rgb", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Mapping", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_attribute_vector_rotate(StructRNA *srna)
+{
+ static const EnumPropertyItem rotate_mode_items[] = {
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS,
+ "AXIS_ANGLE",
+ 0,
+ "Axis Angle",
+ "Rotate a point using axis angle"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X, "X_AXIS", 0, "X Axis", "Rotate a point using X axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y, "Y_AXIS", 0, "Y Axis", "Rotate a point using Y axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z, "Z_AXIS", 0, "Z Axis", "Rotate a point using Z axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ,
+ "EULER_XYZ",
+ 0,
+ "Euler",
+ "Rotate a point using XYZ order"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeVectorRotate", "storage");
+
+ prop = RNA_def_property(srna, "rotation_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "mode");
+ RNA_def_property_enum_items(prop, rotate_mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Type of rotation");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Vector", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_center", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Center", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Axis", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_angle", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
+ RNA_def_property_ui_text(prop, "Input Type Angle", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_rotation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Rotation", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_point_rotate(StructRNA *srna)
{
static const EnumPropertyItem type_items[] = {
@@ -9174,7 +9525,7 @@ static void def_geo_point_scale(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeGeometryPointScale", "storage");
prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float_vector);
RNA_def_property_ui_text(prop, "Input Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
@@ -9191,19 +9542,6 @@ static void def_geo_point_translate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
-static void def_geo_attribute_sample_texture(StructRNA *srna)
-{
- PropertyRNA *prop;
-
- prop = RNA_def_property(srna, "texture", PROP_POINTER, PROP_NONE);
- RNA_def_property_pointer_sdna(prop, NULL, "id");
- RNA_def_property_struct_type(prop, "Texture");
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_ui_text(prop, "Texture", "Texture to sample values from");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations");
-}
-
static void def_geo_object_info(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9471,6 +9809,93 @@ static void def_geo_mesh_line(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_switch(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeSwitch", "storage");
+ prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "input_type");
+ RNA_def_property_enum_items(prop, node_socket_data_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeSwitch_type_itemf");
+ RNA_def_property_ui_text(prop, "Input Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_curve_resample(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static EnumPropertyItem mode_items[] = {
+ {GEO_NODE_CURVE_SAMPLE_COUNT,
+ "COUNT",
+ 0,
+ "Count",
+ "Sample the specified number of points along each spline"},
+ {GEO_NODE_CURVE_SAMPLE_LENGTH,
+ "LENGTH",
+ 0,
+ "Length",
+ "Calculate the number of samples by splitting each spline into segments with the specified "
+ "length"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurveResample", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "How to specify the amount of samples");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_attribute_transfer(StructRNA *srna)
+{
+ static EnumPropertyItem mapping_items[] = {
+ {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED,
+ "NEAREST_FACE_INTERPOLATED",
+ 0,
+ "Nearest Face Interpolated",
+ "Transfer the attribute from the nearest face on a surface (loose points and edges are "
+ "ignored)"},
+ {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST,
+ "NEAREST",
+ 0,
+ "Nearest",
+ "Transfer the element from the nearest element (using face and edge centers for the "
+ "distance computation)"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeTransfer", "storage");
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items);
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO);
+ RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mapping_items);
+ RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_input_material(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(prop, "Material", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -9566,6 +9991,11 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Identifier", "Unique identifier for mapping sockets");
+ prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "description");
+ RNA_def_property_ui_text(prop, "Tooltip", "Socket tooltip");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocket_update");
+
prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_NodeSocket_is_output_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -9594,6 +10024,12 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Linked", "True if the socket is connected");
+ prop = RNA_def_property(srna, "is_multi_input", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SOCK_MULTI_INPUT);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "Multi Input", "True if the socket can accept multiple ordered input links");
+
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SOCK_COLLAPSED);
@@ -9704,6 +10140,11 @@ static void rna_def_node_socket_interface(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Identifier", "Unique identifier for mapping sockets");
+ prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "description");
+ RNA_def_property_ui_text(prop, "Tooltip", "Socket tooltip");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+
prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_NodeSocket_is_output_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -10227,6 +10668,80 @@ static void rna_def_node_socket_collection(BlenderRNA *brna,
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
}
+static void rna_def_node_socket_texture(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, "Texture Node Socket", "Texture socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "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, "Texture");
+ 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, "Texture Node Socket Interface", "Texture socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "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, "Texture");
+ 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_material(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, "Material Node Socket", "Material socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "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, "Material");
+ RNA_def_property_pointer_funcs(
+ prop, NULL, NULL, NULL, "rna_NodeSocketMaterial_default_value_poll");
+ 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, "Material Node Socket Interface", "Material socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "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, "Material");
+ RNA_def_property_pointer_funcs(
+ prop, NULL, NULL, NULL, "rna_NodeSocketMaterial_default_value_poll");
+ 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,
@@ -10326,6 +10841,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_float(
brna, "NodeSocketFloatTime", "NodeSocketInterfaceFloatTime", PROP_TIME);
rna_def_node_socket_float(
+ brna, "NodeSocketFloatTimeAbsolute", "NodeSocketInterfaceFloatTimeAbsolute", PROP_TIME_ABSOLUTE);
+ rna_def_node_socket_float(
brna, "NodeSocketFloatDistance", "NodeSocketInterfaceFloatDistance", PROP_DISTANCE);
rna_def_node_socket_int(brna, "NodeSocketInt", "NodeSocketInterfaceInt", PROP_NONE);
@@ -10371,6 +10888,10 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry");
rna_def_node_socket_collection(brna, "NodeSocketCollection", "NodeSocketInterfaceCollection");
+
+ rna_def_node_socket_texture(brna, "NodeSocketTexture", "NodeSocketInterfaceTexture");
+
+ rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial");
}
static void rna_def_internal_node(BlenderRNA *brna)
@@ -10654,6 +11175,12 @@ static void rna_def_node(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Show Texture", "Display node in viewport textured shading mode");
RNA_def_property_update(prop, 0, "rna_Node_update");
+ prop = RNA_def_property(srna, "active_preview", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_ACTIVE_PREVIEW);
+ RNA_def_property_ui_text(prop, "Active Preview", "Node is previewed in other editor");
+ RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
+ RNA_def_property_update(prop, NC_NODE, NULL);
+
/* generic property update function */
func = RNA_def_function(srna, "socket_value_update", "rna_Node_socket_value_update");
RNA_def_function_ui_description(func, "Update after property changes");
@@ -11149,6 +11676,14 @@ static void rna_def_nodetree(BlenderRNA *brna)
parm = RNA_def_pointer(
func, "result_3", "ID", "From ID", "Original ID data-block selected from the context");
RNA_def_function_output(func, parm);
+
+ /* Check for support of a socket type. */
+ func = RNA_def_function(srna, "valid_socket_type", NULL);
+ RNA_def_function_ui_description(func, "Check if the socket type is valid for the node tree");
+ RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
+ parm = RNA_def_enum(func, "type", node_socket_type_items, 0, "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_function_return(func, RNA_def_boolean(func, "valid", false, "", ""));
}
static void rna_def_composite_nodetree(BlenderRNA *brna)
@@ -11162,6 +11697,12 @@ static void rna_def_composite_nodetree(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "bNodeTree");
RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS);
+ prop = RNA_def_property(srna, "execution_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "execution_mode");
+ RNA_def_property_enum_items(prop, rna_enum_execution_mode_items);
+ RNA_def_property_ui_text(prop, "Execution Mode", "Set how compositing is executed");
+ RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update");
+
prop = RNA_def_property(srna, "render_quality", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "render_quality");
RNA_def_property_enum_items(prop, node_quality_items);
@@ -11386,6 +11927,12 @@ void RNA_def_nodetree(BlenderRNA *brna)
"Custom Group",
"Base node type for custom registered node group types",
"rna_NodeCustomGroup_register");
+ def_custom_group(brna,
+ "GeometryNodeCustomGroup",
+ "GeometryNode",
+ "Geometry Custom Group",
+ "Custom Geometry Group Node for Python nodes",
+ "rna_GeometryNodeCustomGroup_register");
/* special socket types */
rna_def_cmp_output_file_slot_file(brna);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index b53318cfbfe..b339682222c 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -62,6 +62,8 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "DEG_depsgraph_query.h"
+
const EnumPropertyItem rna_enum_object_mode_items[] = {
{OB_MODE_OBJECT, "OBJECT", ICON_OBJECT_DATAMODE, "Object Mode", ""},
{OB_MODE_EDIT, "EDIT", ICON_EDITMODE_HLT, "Edit Mode", ""},
@@ -84,7 +86,7 @@ const EnumPropertyItem rna_enum_object_mode_items[] = {
{OB_MODE_PAINT_GPENCIL,
"PAINT_GPENCIL",
ICON_GREASEPENCIL,
- "Draw",
+ "Draw Mode",
"Paint Grease Pencil Strokes"},
{OB_MODE_WEIGHT_GPENCIL,
"WEIGHT_GPENCIL",
@@ -1253,10 +1255,16 @@ static int rna_Object_rotation_4d_editable(PointerRNA *ptr, int index)
return PROP_EDITABLE;
}
+static int rna_MaterialSlot_index(PointerRNA *ptr)
+{
+ /* There is an offset of one, so that `ptr->data` is not null. */
+ return POINTER_AS_INT(ptr->data) - 1;
+}
+
static int rna_MaterialSlot_material_editable(PointerRNA *ptr, const char **UNUSED(r_info))
{
Object *ob = (Object *)ptr->owner_id;
- const int index = (Material **)ptr->data - ob->mat;
+ const int index = rna_MaterialSlot_index(ptr);
bool is_editable;
if ((ob->matbits == NULL) || ob->matbits[index]) {
@@ -1273,9 +1281,14 @@ static PointerRNA rna_MaterialSlot_material_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- const int index = (Material **)ptr->data - ob->mat;
+ const int index = rna_MaterialSlot_index(ptr);
- ma = BKE_object_material_get(ob, index + 1);
+ if (DEG_is_evaluated_object(ob)) {
+ ma = BKE_object_material_get_eval(ob, index + 1);
+ }
+ else {
+ ma = BKE_object_material_get(ob, index + 1);
+ }
return rna_pointer_inherit_refine(ptr, &RNA_Material, ma);
}
@@ -1284,7 +1297,7 @@ static void rna_MaterialSlot_material_set(PointerRNA *ptr,
struct ReportList *UNUSED(reports))
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
BLI_assert(BKE_id_is_in_global_main(&ob->id));
BLI_assert(BKE_id_is_in_global_main(value.data));
@@ -1309,15 +1322,17 @@ static bool rna_MaterialSlot_material_poll(PointerRNA *ptr, PointerRNA value)
static int rna_MaterialSlot_link_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
-
- return ob->matbits[index] != 0;
+ int index = rna_MaterialSlot_index(ptr);
+ if (index < ob->totcol) {
+ return ob->matbits[index] != 0;
+ }
+ return false;
}
static void rna_MaterialSlot_link_set(PointerRNA *ptr, int value)
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
if (value) {
ob->matbits[index] = 1;
@@ -1335,7 +1350,7 @@ static int rna_MaterialSlot_name_length(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
ma = BKE_object_material_get(ob, index + 1);
@@ -1350,7 +1365,7 @@ static void rna_MaterialSlot_name_get(PointerRNA *ptr, char *str)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
ma = BKE_object_material_get(ob, index + 1);
@@ -1373,10 +1388,49 @@ static void rna_MaterialSlot_update(Main *bmain, Scene *scene, PointerRNA *ptr)
static char *rna_MaterialSlot_path(PointerRNA *ptr)
{
+ int index = rna_MaterialSlot_index(ptr);
+ return BLI_sprintfN("material_slots[%d]", index);
+}
+
+static int rna_Object_material_slots_length(PointerRNA *ptr)
+{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ if (DEG_is_evaluated_object(ob)) {
+ return BKE_object_material_count_eval(ob);
+ }
+ else {
+ return ob->totcol;
+ }
+}
- return BLI_sprintfN("material_slots[%d]", index);
+static void rna_Object_material_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ const int length = rna_Object_material_slots_length(ptr);
+ iter->internal.count.item = 0;
+ iter->internal.count.ptr = ptr->owner_id;
+ iter->valid = length > 0;
+}
+
+static void rna_Object_material_slots_next(CollectionPropertyIterator *iter)
+{
+ const int length = rna_Object_material_slots_length(&iter->ptr);
+ iter->internal.count.item++;
+ iter->valid = iter->internal.count.item < length;
+}
+
+static PointerRNA rna_Object_material_slots_get(CollectionPropertyIterator *iter)
+{
+ PointerRNA ptr;
+ RNA_pointer_create((ID *)iter->internal.count.ptr,
+ &RNA_MaterialSlot,
+ /* Add one, so that `ptr->data` is not null. */
+ POINTER_FROM_INT(iter->internal.count.item + 1),
+ &ptr);
+ return ptr;
+}
+
+static void rna_Object_material_slots_end(CollectionPropertyIterator *UNUSED(iter))
+{
}
static PointerRNA rna_Object_display_get(PointerRNA *ptr)
@@ -1448,11 +1502,6 @@ static PointerRNA rna_Object_field_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- /* weak */
- if (!ob->pd) {
- ob->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, ob->pd);
}
@@ -1464,11 +1513,6 @@ static PointerRNA rna_Object_collision_get(PointerRNA *ptr)
return PointerRNA_NULL;
}
- /* weak */
- if (!ob->pd) {
- ob->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_CollisionSettings, ob->pd);
}
@@ -2106,6 +2150,81 @@ static void rna_object_lineart_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ptr->owner_id);
}
+static bool mesh_symmetry_get_common(PointerRNA *ptr, const eMeshSymmetryType sym)
+{
+ const Object *ob = (Object *)ptr->owner_id;
+ if (ob->type != OB_MESH) {
+ return false;
+ }
+
+ const Mesh *mesh = ob->data;
+ return mesh->symmetry & sym;
+}
+
+static bool rna_Object_mesh_symmetry_x_get(PointerRNA *ptr)
+{
+ return mesh_symmetry_get_common(ptr, ME_SYMMETRY_X);
+}
+
+static bool rna_Object_mesh_symmetry_y_get(PointerRNA *ptr)
+{
+ return mesh_symmetry_get_common(ptr, ME_SYMMETRY_Y);
+}
+
+static bool rna_Object_mesh_symmetry_z_get(PointerRNA *ptr)
+{
+ return mesh_symmetry_get_common(ptr, ME_SYMMETRY_Z);
+}
+
+static void mesh_symmetry_set_common(PointerRNA *ptr,
+ const bool value,
+ const eMeshSymmetryType sym)
+{
+ Object *ob = (Object *)ptr->owner_id;
+ if (ob->type != OB_MESH) {
+ return;
+ }
+
+ Mesh *mesh = ob->data;
+ if (value) {
+ mesh->symmetry |= sym;
+ }
+ else {
+ mesh->symmetry &= ~sym;
+ }
+}
+
+static void rna_Object_mesh_symmetry_x_set(PointerRNA *ptr, bool value)
+{
+ mesh_symmetry_set_common(ptr, value, ME_SYMMETRY_X);
+}
+
+static void rna_Object_mesh_symmetry_y_set(PointerRNA *ptr, bool value)
+{
+ mesh_symmetry_set_common(ptr, value, ME_SYMMETRY_Y);
+}
+
+static void rna_Object_mesh_symmetry_z_set(PointerRNA *ptr, bool value)
+{
+ mesh_symmetry_set_common(ptr, value, ME_SYMMETRY_Z);
+}
+
+static int rna_Object_mesh_symmetry_yz_editable(PointerRNA *ptr, const char **UNUSED(r_info))
+{
+ const Object *ob = (Object *)ptr->owner_id;
+ if (ob->type != OB_MESH) {
+ return 0;
+ }
+
+ const Mesh *mesh = ob->data;
+ if (ob->mode == OB_MODE_WEIGHT_PAINT && mesh->editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) {
+ /* Only X symmetry is available in weightpaint mode. */
+ return 0;
+ }
+
+ return PROP_EDITABLE;
+}
+
#else
static void rna_def_vertex_group(BlenderRNA *brna)
@@ -2798,6 +2917,7 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_multi_array(prop, 2, boundbox_dimsize);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_float_funcs(prop, "rna_Object_boundbox_get", NULL, NULL);
RNA_def_property_ui_text(
prop,
@@ -2882,12 +3002,18 @@ static void rna_def_object(BlenderRNA *brna)
/* materials */
prop = RNA_def_property(srna, "material_slots", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
RNA_def_property_struct_type(prop, "MaterialSlot");
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_PROP_NAME);
- /* don't dereference pointer! */
- RNA_def_property_collection_funcs(
- prop, NULL, NULL, NULL, "rna_iterator_array_get", NULL, NULL, NULL, NULL);
+ /* Don't dereference the material slot pointer, it is the slot index encoded in a pointer. */
+ RNA_def_property_collection_funcs(prop,
+ "rna_Object_material_slots_begin",
+ "rna_Object_material_slots_next",
+ "rna_Object_material_slots_end",
+ "rna_Object_material_slots_get",
+ "rna_Object_material_slots_length",
+ NULL,
+ NULL,
+ NULL);
RNA_def_property_ui_text(prop, "Material Slots", "Material slots in the object");
prop = RNA_def_property(srna, "active_material", PROP_POINTER, PROP_NONE);
@@ -2968,12 +3094,11 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_float_funcs(
prop, "rna_Object_dimensions_get", "rna_Object_dimensions_set", NULL);
RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
- RNA_def_property_ui_text(
- prop,
- "Dimensions",
- "Absolute bounding box dimensions of the object (WARNING: assigning to it or "
- "its members multiple consecutive times will not work correctly, "
- "as this needs up-to-date evaluated data)");
+ RNA_def_property_ui_text(prop,
+ "Dimensions",
+ "Absolute bounding box dimensions of the object.\n"
+ "Warning: Assigning to it or its members multiple consecutive times "
+ "will not work correctly, as this needs up-to-date evaluated data");
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update");
/* delta transforms */
@@ -3076,8 +3201,8 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Local Matrix",
- "Parent relative transformation matrix - "
- "WARNING: Only takes into account 'Object' parenting, so e.g. in case of bone parenting "
+ "Parent relative transformation matrix.\n"
+ "Warning: Only takes into account object parenting, so e.g. in case of bone parenting "
"you get a matrix relative to the Armature object, not to the actual parent bone");
RNA_def_property_float_funcs(
prop, "rna_Object_matrix_local_get", "rna_Object_matrix_local_set", NULL);
@@ -3499,6 +3624,28 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "ObjectLineArt");
RNA_def_property_ui_text(prop, "Line Art", "Line art settings for the object");
+ /* Mesh Symmetry Settings */
+
+ prop = RNA_def_property(srna, "use_mesh_mirror_x", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Object_mesh_symmetry_x_get", "rna_Object_mesh_symmetry_x_set");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "X", "Enable mesh symmetry in the X axis");
+
+ prop = RNA_def_property(srna, "use_mesh_mirror_y", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Object_mesh_symmetry_y_get", "rna_Object_mesh_symmetry_y_set");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_editable_func(prop, "rna_Object_mesh_symmetry_yz_editable");
+ RNA_def_property_ui_text(prop, "Y", "Enable mesh symmetry in the Y axis");
+
+ prop = RNA_def_property(srna, "use_mesh_mirror_z", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Object_mesh_symmetry_z_get", "rna_Object_mesh_symmetry_z_set");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_editable_func(prop, "rna_Object_mesh_symmetry_yz_editable");
+ RNA_def_property_ui_text(prop, "Z", "Enable mesh symmetry in the Z axis");
+
RNA_define_lib_overridable(false);
/* anim */
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index df628caa000..e463323c6dc 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -296,8 +296,8 @@ static bool rna_Object_visible_in_viewport_get(Object *ob, View3D *v3d)
static void rna_Object_mat_convert_space(Object *ob,
ReportList *reports,
bPoseChannel *pchan,
- float *mat,
- float *mat_ret,
+ float mat[16],
+ float mat_ret[16],
int from,
int to)
{
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index 14dd8a68fee..eb7da0d0ce2 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -648,6 +648,13 @@ static void rna_FieldSettings_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
ob->pd->tex = NULL;
}
+ /* In the case of specific force-fields that are using the #EffectorData's normal, we need to
+ * rebuild mesh and BVH-tree for #SurfaceModifier to work correctly. */
+ if (ELEM(ob->pd->shape, PFIELD_SHAPE_SURFACE, PFIELD_SHAPE_POINTS) ||
+ ob->pd->forcefield == PFIELD_GUIDE) {
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
}
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index d94e68a6808..7ff2a82a465 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -1413,24 +1413,12 @@ static const EnumPropertyItem *rna_Particle_ren_as_itemf(bContext *UNUSED(C),
static PointerRNA rna_Particle_field1_get(PointerRNA *ptr)
{
ParticleSettings *part = (ParticleSettings *)ptr->owner_id;
-
- /* weak */
- if (!part->pd) {
- part->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, part->pd);
}
static PointerRNA rna_Particle_field2_get(PointerRNA *ptr)
{
ParticleSettings *part = (ParticleSettings *)ptr->owner_id;
-
- /* weak */
- if (!part->pd2) {
- part->pd2 = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, part->pd2);
}
diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c
index ba65e42895c..b8bb4f58dcd 100644
--- a/source/blender/makesrna/intern/rna_pose.c
+++ b/source/blender/makesrna/intern/rna_pose.c
@@ -1359,12 +1359,27 @@ static void rna_def_pose_channel(BlenderRNA *brna)
RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_dependency_update");
- prop = RNA_def_property(srna, "custom_shape_scale", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "custom_scale");
- RNA_def_property_range(prop, 0.0f, 1000.0f);
+ prop = RNA_def_property(srna, "custom_shape_scale_xyz", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "custom_scale_xyz");
+ RNA_def_property_flag(prop, PROP_PROPORTIONAL);
+ RNA_def_property_float_array_default(prop, rna_default_scale_3d);
RNA_def_property_ui_text(prop, "Custom Shape Scale", "Adjust the size of the custom shape");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+ prop = RNA_def_property(srna, "custom_shape_translation", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "custom_translation");
+ RNA_def_property_flag(prop, PROP_PROPORTIONAL);
+ RNA_def_property_ui_text(
+ prop, "Custom Shape Translation", "Adjust the location of the custom shape");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+
+ prop = RNA_def_property(srna, "custom_shape_rotation_euler", PROP_FLOAT, PROP_EULER);
+ RNA_def_property_float_sdna(prop, NULL, "custom_rotation_euler");
+ RNA_def_property_ui_text(
+ prop, "Custom Shape Rotation", "Adjust the rotation of the custom shape");
+ RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+
prop = RNA_def_property(srna, "use_custom_shape_bone_size", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "drawflag", PCHAN_DRAW_NO_CUSTOM_BONE_SIZE);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c
index 29516830058..0d35365c2d8 100644
--- a/source/blender/makesrna/intern/rna_pose_api.c
+++ b/source/blender/makesrna/intern/rna_pose_api.c
@@ -47,7 +47,7 @@
# include "BLI_ghash.h"
-static float rna_PoseBone_do_envelope(bPoseChannel *chan, float *vec)
+static float rna_PoseBone_do_envelope(bPoseChannel *chan, float vec[3])
{
Bone *bone = chan->bone;
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 0411ef6d6ee..dd91a5509f5 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -298,13 +298,13 @@ static void rna_RenderEngine_unregister(Main *bmain, StructRNA *type)
return;
}
+ /* Stop all renders in case we were using this one. */
+ ED_render_engine_changed(bmain, false);
RE_FreeAllPersistentData();
+
RNA_struct_free_extension(type, &et->rna_ext);
RNA_struct_free(&BLENDER_RNA, type);
BLI_freelinkN(&R_engines, et);
-
- /* Stop all renders in case we were using this one. */
- ED_render_engine_changed(bmain, false);
}
static StructRNA *rna_RenderEngine_register(Main *bmain,
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index d8336e79064..9e7d0f99dfa 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -20,6 +20,8 @@
#include <stdlib.h>
+#include <CLG_log.h>
+
#include "DNA_ID.h"
#include "BLI_utildefines.h"
@@ -79,7 +81,16 @@ const EnumPropertyItem rna_enum_property_subtype_items[] = {
{PROP_PERCENTAGE, "PERCENTAGE", 0, "Percentage", ""},
{PROP_FACTOR, "FACTOR", 0, "Factor", ""},
{PROP_ANGLE, "ANGLE", 0, "Angle", ""},
- {PROP_TIME, "TIME", 0, "Time", ""},
+ {PROP_TIME,
+ "TIME",
+ 0,
+ "Time (Scene Relative)",
+ "Time specified in frames, converted to seconds based on scene frame rate"},
+ {PROP_TIME_ABSOLUTE,
+ "TIME_ABSOLUTE",
+ 0,
+ "Time (Absolute)",
+ "Time specified in seconds, independent of the scene"},
{PROP_DISTANCE, "DISTANCE", 0, "Distance", ""},
{PROP_DISTANCE_CAMERA, "DISTANCE_CAMERA", 0, "Camera Distance", ""},
{PROP_POWER, "POWER", 0, "Power", ""},
@@ -132,6 +143,8 @@ const EnumPropertyItem rna_enum_property_unit_items[] = {
# include "BKE_idprop.h"
# include "BKE_lib_override.h"
+static CLG_LogRef LOG_COMPARE_OVERRIDE = {"rna.rna_compare_override"};
+
/* Struct */
static void rna_Struct_identifier_get(PointerRNA *ptr, char *value)
@@ -1366,11 +1379,11 @@ static int rna_property_override_diff_propptr(Main *bmain,
/* In case one of the owner of the checked property is tagged as needing resync, do
* not change the 'match reference' status of its ID pointer properties overrides,
* since many non-matching ones are likely due to missing resync. */
- printf(
- "%s: Not checking matching ID pointer properties, since owner %s is tagged as "
- "needing resync.\n",
- __func__,
- id_a->name);
+ CLOG_INFO(&LOG_COMPARE_OVERRIDE,
+ 4,
+ "Not checking matching ID pointer properties, since owner %s is tagged as "
+ "needing resync.\n",
+ id_a->name);
}
else if (id_a->override_library != NULL && id_a->override_library->reference == id_b) {
opop->flag |= IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE;
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 3344b8c286d..2ccc8fe949c 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -778,7 +778,6 @@ static void rna_Scene_objects_begin(CollectionPropertyIterator *iter, PointerRNA
Scene *scene = (Scene *)ptr->data;
iter->internal.custom = MEM_callocN(sizeof(BLI_Iterator), __func__);
- ((BLI_Iterator *)iter->internal.custom)->valid = true;
BKE_scene_objects_iterator_begin(iter->internal.custom, (void *)scene);
iter->valid = ((BLI_Iterator *)iter->internal.custom)->valid;
}
@@ -1896,10 +1895,10 @@ static void rna_Scene_use_persistent_data_update(Main *UNUSED(bmain),
Scene *UNUSED(scene),
PointerRNA *ptr)
{
- Scene *sce = (Scene *)ptr->owner_id;
+ Scene *scene = (Scene *)ptr->owner_id;
- if (!(sce->r.mode & R_PERSISTENT_DATA)) {
- RE_FreePersistentData();
+ if (!(scene->r.mode & R_PERSISTENT_DATA)) {
+ RE_FreePersistentData(scene);
}
}
@@ -1913,10 +1912,9 @@ static void rna_Scene_transform_orientation_slots_begin(CollectionPropertyIterat
iter, orient_slot, sizeof(*orient_slot), ARRAY_SIZE(scene->orientation_slots), 0, NULL);
}
-static int rna_Scene_transform_orientation_slots_length(PointerRNA *ptr)
+static int rna_Scene_transform_orientation_slots_length(PointerRNA *UNUSED(ptr))
{
- Scene *scene = (Scene *)ptr->owner_id;
- return ARRAY_SIZE(scene->orientation_slots);
+ return ARRAY_SIZE(((Scene *)NULL)->orientation_slots);
}
static bool rna_Scene_use_audio_get(PointerRNA *ptr)
@@ -2484,6 +2482,10 @@ const EnumPropertyItem *rna_TransformOrientation_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return rna_enum_transform_orientation_items;
+ }
+
Scene *scene;
if (ptr->owner_id && (GS(ptr->owner_id->name) == ID_SCE)) {
scene = (Scene *)ptr->owner_id;
@@ -2494,11 +2496,15 @@ const EnumPropertyItem *rna_TransformOrientation_itemf(bContext *C,
return rna_TransformOrientation_impl_itemf(scene, false, r_free);
}
-const EnumPropertyItem *rna_TransformOrientation_with_scene_itemf(bContext *UNUSED(C),
+const EnumPropertyItem *rna_TransformOrientation_with_scene_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return rna_enum_transform_orientation_items;
+ }
+
Scene *scene = (Scene *)ptr->owner_id;
TransformOrientationSlot *orient_slot = ptr->data;
bool include_default = (orient_slot != &scene->orientation_slots[SCE_ORIENT_DEFAULT]);
@@ -2685,6 +2691,7 @@ static void rna_def_view3d_cursor(BlenderRNA *brna)
RNA_def_struct_path_func(srna, "rna_View3DCursor_path");
RNA_def_struct_ui_text(srna, "3D Cursor", "");
RNA_def_struct_ui_icon(srna, ICON_CURSOR);
+ RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_XYZ_LENGTH);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -2855,7 +2862,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "auto_normalize", 1);
RNA_def_property_ui_text(prop,
- "WPaint Auto-Normalize",
+ "Weight Paint Auto-Normalize",
"Ensure all bone-deforming vertex groups add up "
"to 1.0 while weight painting");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
@@ -2864,7 +2871,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "wpaint_lock_relative", 1);
RNA_def_property_ui_text(prop,
- "WPaint Lock-Relative",
+ "Weight Paint Lock-Relative",
"Display bone-deforming groups as if all locked deform groups "
"were deleted, and the remaining ones were re-normalized");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
@@ -2873,7 +2880,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "multipaint", 1);
RNA_def_property_ui_text(prop,
- "WPaint Multi-Paint",
+ "Weight Paint Multi-Paint",
"Paint across the weights of all selected bones, "
"maintaining their relative influence");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
@@ -6605,8 +6612,10 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
/* persistent data */
prop = RNA_def_property(srna, "use_persistent_data", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "mode", R_PERSISTENT_DATA);
- RNA_def_property_ui_text(
- prop, "Persistent Data", "Keep render data around for faster re-renders");
+ RNA_def_property_ui_text(prop,
+ "Persistent Data",
+ "Keep render data around for faster re-renders and animation renders, "
+ "at the cost of increased memory usage");
RNA_def_property_update(prop, 0, "rna_Scene_use_persistent_data_update");
/* Freestyle line thickness options */
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index c2089004da2..c49b41867a8 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -98,7 +98,7 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf
}
}
-static void rna_Scene_uvedit_aspect(Scene *UNUSED(scene), Object *ob, float *aspect)
+static void rna_Scene_uvedit_aspect(Scene *UNUSED(scene), Object *ob, float aspect[2])
{
if ((ob->type == OB_MESH) && (ob->mode == OB_MODE_EDIT)) {
BMEditMesh *em;
diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c
index 6cf1d7a923b..fba1d3cda9a 100644
--- a/source/blender/makesrna/intern/rna_screen.c
+++ b/source/blender/makesrna/intern/rna_screen.c
@@ -112,18 +112,6 @@ static bool rna_Screen_fullscreen_get(PointerRNA *ptr)
return (screen->state == SCREENMAXIMIZED);
}
-/* UI compatible list: should not be needed, but for now we need to keep EMPTY
- * at least in the static version of this enum for python scripts. */
-static const EnumPropertyItem *rna_Area_type_itemf(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
- bool *r_free)
-{
- /* +1 to skip SPACE_EMPTY */
- *r_free = false;
- return rna_enum_space_type_items + 1;
-}
-
static int rna_Area_type_get(PointerRNA *ptr)
{
ScrArea *area = (ScrArea *)ptr->data;
@@ -142,6 +130,11 @@ static void rna_Area_type_set(PointerRNA *ptr, int value)
}
ScrArea *area = (ScrArea *)ptr->data;
+ /* Empty areas are locked. */
+ if ((value == SPACE_EMPTY) || (area->spacetype == SPACE_EMPTY)) {
+ return;
+ }
+
area->butspacetype = value;
}
@@ -188,16 +181,20 @@ static void rna_Area_type_update(bContext *C, PointerRNA *ptr)
}
static const EnumPropertyItem *rna_Area_ui_type_itemf(bContext *C,
- PointerRNA *UNUSED(ptr),
+ PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
EnumPropertyItem *item = NULL;
int totitem = 0;
- /* +1 to skip SPACE_EMPTY */
- for (const EnumPropertyItem *item_from = rna_enum_space_type_items + 1; item_from->identifier;
- item_from++) {
+ ScrArea *area = (ScrArea *)ptr->data;
+ const EnumPropertyItem *item_from = rna_enum_space_type_items;
+ if (area->spacetype != SPACE_EMPTY) {
+ item_from += 1; /* +1 to skip SPACE_EMPTY */
+ }
+
+ for (; item_from->identifier; item_from++) {
if (ELEM(item_from->value, SPACE_TOPBAR, SPACE_STATUSBAR)) {
continue;
}
@@ -224,6 +221,10 @@ static const EnumPropertyItem *rna_Area_ui_type_itemf(bContext *C,
static int rna_Area_ui_type_get(PointerRNA *ptr)
{
ScrArea *area = ptr->data;
+ /* This is for the Python API which may inspect empty areas. */
+ if (UNLIKELY(area->spacetype == SPACE_EMPTY)) {
+ return SPACE_EMPTY;
+ }
const int area_type = rna_Area_type_get(ptr);
const bool area_changing = area->butspacetype != SPACE_EMPTY;
int value = area_type << 16;
@@ -252,6 +253,10 @@ static void rna_Area_ui_type_set(PointerRNA *ptr, int value)
{
ScrArea *area = ptr->data;
const int space_type = value >> 16;
+ /* Empty areas are locked. */
+ if ((space_type == SPACE_EMPTY) || (area->spacetype == SPACE_EMPTY)) {
+ return;
+ }
SpaceType *st = BKE_spacetype_from_id(space_type);
rna_Area_type_set(ptr, space_type);
@@ -380,12 +385,17 @@ static void rna_def_area(BlenderRNA *brna)
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", HEADER_NO_PULLDOWN);
RNA_def_property_ui_text(prop, "Show Menus", "Show menus in the header");
+ /* Note on space type use of #SPACE_EMPTY, this is not visible to the user,
+ * and script authors should be able to assign this value, however the value may be set
+ * and needs to be read back by script authors.
+ *
+ * This happens when an area is full-screen (when #ScrArea.full is set).
+ * in this case reading the empty value is needed, but it should never be set, see: T87187. */
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "spacetype");
RNA_def_property_enum_items(prop, rna_enum_space_type_items);
RNA_def_property_enum_default(prop, SPACE_VIEW3D);
- RNA_def_property_enum_funcs(
- prop, "rna_Area_type_get", "rna_Area_type_set", "rna_Area_type_itemf");
+ RNA_def_property_enum_funcs(prop, "rna_Area_type_get", "rna_Area_type_set", NULL);
RNA_def_property_ui_text(prop, "Editor Type", "Current editor type for this area");
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 4481555b931..81acedb76f2 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -786,7 +786,8 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_ShowMask_update");
prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_PIXEL);
- RNA_def_property_ui_range(prop, 0.5, 40.0, 10, 2);
+ RNA_def_property_ui_range(prop, 0.5, 40.0, 0.1, 2);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC);
RNA_def_property_ui_text(
prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (in pixels)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 24d051fecc8..2652003cb1c 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -293,7 +293,7 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq)
if (SEQ_transform_test_overlap(seqbase, seq)) {
SEQ_transform_seqbase_shuffle(seqbase, seq, scene); /* XXX - BROKEN!, uses context seqbasep */
}
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
}
/* A simple wrapper around above func, directly usable as prop update func.
@@ -440,8 +440,7 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value)
Scene *scene = (Scene *)ptr->owner_id;
SEQ_relations_invalidate_cache_composite(scene, seq);
- SEQ_transform_set_right_handle_frame(seq,
- SEQ_transform_get_left_handle_frame(seq, false) + value);
+ SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) + value);
do_sequence_frame_change_update(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
@@ -449,8 +448,7 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value)
static int rna_Sequence_frame_length_get(PointerRNA *ptr)
{
Sequence *seq = (Sequence *)ptr->data;
- return SEQ_transform_get_right_handle_frame(seq, false) -
- SEQ_transform_get_left_handle_frame(seq, false);
+ return SEQ_transform_get_right_handle_frame(seq) - SEQ_transform_get_left_handle_frame(seq);
}
static int rna_Sequence_frame_editable(PointerRNA *ptr, const char **UNUSED(r_info))
@@ -476,7 +474,7 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value)
/* XXX - BROKEN!, uses context seqbasep */
SEQ_transform_seqbase_shuffle_ex(seqbase, seq, scene, channel_delta);
}
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
@@ -505,7 +503,7 @@ static Sequence *sequence_get_by_transform(Editing *ed, StripTransform *transfor
data.data = transform;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, transform_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, transform_seq_cmp_fn, &data);
return data.seq;
}
@@ -557,7 +555,7 @@ static Sequence *sequence_get_by_crop(Editing *ed, StripCrop *crop)
data.data = crop;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, crop_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, crop_seq_cmp_fn, &data);
return data.seq;
}
@@ -951,7 +949,7 @@ static Sequence *sequence_get_by_proxy(Editing *ed, StripProxy *proxy)
data.seq = NULL;
data.data = proxy;
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_fn, &data);
return data.seq;
}
@@ -990,18 +988,14 @@ static int colbalance_seq_cmp_fn(Sequence *seq, void *arg_pt)
{
SequenceSearchData *data = arg_pt;
- if (seq->modifiers.first) {
- SequenceModifierData *smd = seq->modifiers.first;
+ for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) {
+ if (smd->type == seqModifierType_ColorBalance) {
+ ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd;
- for (smd = seq->modifiers.first; smd; smd = smd->next) {
- if (smd->type == seqModifierType_ColorBalance) {
- ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd;
-
- if (&cbmd->color_balance == data->data) {
- data->seq = seq;
- data->smd = smd;
- return -1; /* done so bail out */
- }
+ if (&cbmd->color_balance == data->data) {
+ data->seq = seq;
+ data->smd = smd;
+ return -1; /* done so bail out */
}
}
}
@@ -1020,7 +1014,7 @@ static Sequence *sequence_get_by_colorbalance(Editing *ed,
data.data = cb;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, colbalance_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, colbalance_seq_cmp_fn, &data);
*r_smd = data.smd;
@@ -1144,7 +1138,7 @@ static Sequence *sequence_get_by_modifier(Editing *ed, SequenceModifierData *smd
data.data = smd;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, modifier_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, modifier_seq_cmp_fn, &data);
return data.seq;
}
@@ -1350,6 +1344,11 @@ static void rna_def_strip_element(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "orig_height");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Orig Height", "Original image height");
+
+ prop = RNA_def_property(srna, "orig_fps", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "orig_fps");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second");
}
static void rna_def_strip_crop(BlenderRNA *brna)
@@ -1517,7 +1516,7 @@ static void rna_def_strip_proxy(BlenderRNA *brna)
prop = RNA_def_property(srna, "quality", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "quality");
- RNA_def_property_ui_text(prop, "Quality", "JPEG Quality of proxies to build");
+ RNA_def_property_ui_text(prop, "Quality", "Quality of proxies to build");
RNA_def_property_ui_range(prop, 1, 100, 1, -1);
prop = RNA_def_property(srna, "timecode", PROP_ENUM, PROP_NONE);
@@ -2394,11 +2393,10 @@ static void rna_def_scene(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Input", "Input type to use for the Scene strip");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_use_sequence");
- prop = RNA_def_property(srna, "use_grease_pencil", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_SCENE_NO_GPENCIL);
- RNA_def_property_ui_text(
- prop, "Use Grease Pencil", "Show Grease Pencil strokes in OpenGL previews");
- RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
+ prop = RNA_def_property(srna, "use_annotations", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_SCENE_NO_ANNOTATION);
+ RNA_def_property_ui_text(prop, "Use Annotations", "Show Annotations in OpenGL previews");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
rna_def_filter_video(srna);
rna_def_proxy(srna);
@@ -2417,12 +2415,6 @@ static void rna_def_movie(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Movie Sequence", "Sequence strip to load a video");
RNA_def_struct_sdna(srna, "Sequence");
- prop = RNA_def_property(srna, "mpeg_preseek", PROP_INT, PROP_NONE);
- RNA_def_property_int_sdna(prop, NULL, "anim_preseek");
- RNA_def_property_range(prop, 0, 50);
- RNA_def_property_ui_text(prop, "MPEG Preseek", "For MPEG movies, preseek this many frames");
- RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
-
prop = RNA_def_property(srna, "stream_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "streamindex");
RNA_def_property_range(prop, 0, 20);
@@ -2930,7 +2922,7 @@ static void rna_def_text(StructRNA *srna)
prop = RNA_def_property(srna, "use_box", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TEXT_BOX);
- RNA_def_property_ui_text(prop, "Shadow", "Display colored box behind text");
+ RNA_def_property_ui_text(prop, "Box", "Display colored box behind text");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_bold", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index a49a404fe6c..8aab0c079a3 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -224,13 +224,15 @@ static Sequence *rna_Sequences_new_image(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
Scene *scene = (Scene *)id;
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.image.len = 1;
+ load_data.fit_method = fit_method;
Sequence *seq = SEQ_add_image_strip(bmain, scene, seqbase, &load_data);
char dir[FILE_MAX], filename[FILE_MAX];
@@ -253,10 +255,11 @@ static Sequence *rna_Sequences_editing_new_image(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
return rna_Sequences_new_image(
- id, &ed->seqbase, bmain, reports, name, file, channel, frame_start);
+ id, &ed->seqbase, bmain, reports, name, file, channel, frame_start, fit_method);
}
static Sequence *rna_Sequences_meta_new_image(ID *id,
@@ -266,10 +269,11 @@ static Sequence *rna_Sequences_meta_new_image(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
return rna_Sequences_new_image(
- id, &seq->seqbase, bmain, reports, name, file, channel, frame_start);
+ id, &seq->seqbase, bmain, reports, name, file, channel, frame_start, fit_method);
}
static Sequence *rna_Sequences_new_movie(ID *id,
@@ -278,11 +282,13 @@ static Sequence *rna_Sequences_new_movie(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
Scene *scene = (Scene *)id;
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
+ load_data.fit_method = fit_method;
load_data.allow_invalid_file = true;
Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data);
@@ -299,9 +305,11 @@ static Sequence *rna_Sequences_editing_new_movie(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
- return rna_Sequences_new_movie(id, &ed->seqbase, bmain, name, file, channel, frame_start);
+ return rna_Sequences_new_movie(
+ id, &ed->seqbase, bmain, name, file, channel, frame_start, fit_method);
}
static Sequence *rna_Sequences_meta_new_movie(ID *id,
@@ -310,9 +318,11 @@ static Sequence *rna_Sequences_meta_new_movie(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
- return rna_Sequences_new_movie(id, &seq->seqbase, bmain, name, file, channel, frame_start);
+ return rna_Sequences_new_movie(
+ id, &seq->seqbase, bmain, name, file, channel, frame_start, fit_method);
}
# ifdef WITH_AUDASPACE
@@ -724,6 +734,18 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastri
{0, NULL, 0, NULL, NULL},
};
+ 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"},
+ {SEQ_USE_ORIGINAL_SIZE, "ORIGINAL", 0, "Use Original Size", "Don't scale the image"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
const char *new_clip_func_name = "rna_Sequences_editing_new_clip";
const char *new_mask_func_name = "rna_Sequences_editing_new_mask";
const char *new_scene_func_name = "rna_Sequences_editing_new_scene";
@@ -849,6 +871,9 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastri
-MAXFRAME,
MAXFRAME);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_enum(
+ func, "fit_method", scale_fit_methods, SEQ_USE_ORIGINAL_SIZE, "Image Fit Method", NULL);
+ RNA_def_parameter_flags(parm, 0, PARM_PYFUNC_OPTIONAL);
/* return type */
parm = RNA_def_pointer(func, "sequence", "Sequence", "", "New Sequence");
RNA_def_function_return(func, parm);
@@ -873,6 +898,9 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastri
-MAXFRAME,
MAXFRAME);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_enum(
+ func, "fit_method", scale_fit_methods, SEQ_USE_ORIGINAL_SIZE, "Image Fit Method", NULL);
+ RNA_def_parameter_flags(parm, 0, PARM_PYFUNC_OPTIONAL);
/* return type */
parm = RNA_def_pointer(func, "sequence", "Sequence", "", "New Sequence");
RNA_def_function_return(func, parm);
diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c
index 451cc56eba7..4a6cb0d740a 100644
--- a/source/blender/makesrna/intern/rna_shader_fx.c
+++ b/source/blender/makesrna/intern/rna_shader_fx.c
@@ -216,6 +216,8 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "BlurShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "radius");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
@@ -240,6 +242,8 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_BLUR_DOF_MODE);
RNA_def_property_ui_text(prop, "Use as Depth Of Field", "Blur using camera depth of field");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_colorize(BlenderRNA *brna)
@@ -252,6 +256,8 @@ static void rna_def_shader_fx_colorize(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ColorizeShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "factor");
RNA_def_property_range(prop, 0, 1.0);
@@ -277,6 +283,8 @@ static void rna_def_shader_fx_colorize(BlenderRNA *brna)
RNA_def_property_enum_items(prop, rna_enum_shaderfx_colorize_modes_items);
RNA_def_property_ui_text(prop, "Mode", "Effect mode");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_wave(BlenderRNA *brna)
@@ -294,6 +302,8 @@ static void rna_def_shader_fx_wave(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "WaveShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "orientation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "orientation");
RNA_def_property_enum_items(prop, prop_shaderfx_wave_type_items);
@@ -317,6 +327,8 @@ static void rna_def_shader_fx_wave(BlenderRNA *brna)
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Phase", "Phase Shift of Wave");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_pixel(BlenderRNA *brna)
@@ -329,6 +341,8 @@ static void rna_def_shader_fx_pixel(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "PixelShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "size");
RNA_def_property_range(prop, 1, SHRT_MAX);
@@ -340,6 +354,8 @@ static void rna_def_shader_fx_pixel(BlenderRNA *brna)
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FX_PIXEL_FILTER_NEAREST);
RNA_def_property_ui_text(prop, "Antialiasing", "Antialias pixels");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_rim(BlenderRNA *brna)
@@ -352,6 +368,8 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "RimShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "offset", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "offset");
RNA_def_property_range(prop, SHRT_MIN, SHRT_MAX);
@@ -392,6 +410,8 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna)
RNA_def_property_int_default(prop, 4);
RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_shadow(BlenderRNA *brna)
@@ -409,11 +429,12 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ShadowShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Object to determine center of rotation");
RNA_def_property_pointer_funcs(prop, NULL, "rna_ShadowShaderFx_object_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_ShaderFx_dependency_update");
prop = RNA_def_property(srna, "offset", PROP_INT, PROP_PIXEL);
@@ -490,6 +511,8 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SHADOW_USE_WAVE);
RNA_def_property_ui_text(prop, "Wave", "Use wave effect");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_glow(BlenderRNA *brna)
@@ -502,6 +525,8 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "GlowShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "glow_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_float_sdna(prop, NULL, "glow_color");
@@ -569,6 +594,8 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna)
RNA_def_property_enum_items(prop, rna_enum_glow_blend_modes_items);
RNA_def_property_ui_text(prop, "Blend Mode", "Blend mode");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_swirl(BlenderRNA *brna)
@@ -581,6 +608,8 @@ static void rna_def_shader_fx_swirl(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SwirlShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "radius", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "radius");
RNA_def_property_range(prop, 0, SHRT_MAX);
@@ -603,8 +632,9 @@ static void rna_def_shader_fx_swirl(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Object", "Object to determine center location");
RNA_def_property_pointer_funcs(prop, NULL, "rna_SwirlShaderFx_object_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_ShaderFx_dependency_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_flip(BlenderRNA *brna)
@@ -617,6 +647,8 @@ static void rna_def_shader_fx_flip(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "FlipShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "flip_horizontal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_HORIZONTAL);
RNA_def_property_ui_text(prop, "Horizontal", "Flip image horizontally");
@@ -626,6 +658,8 @@ static void rna_def_shader_fx_flip(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_VERTICAL);
RNA_def_property_ui_text(prop, "Vertical", "Flip image vertically");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
void RNA_def_shader_fx(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index f9b1816e1ba..39edc6c3b9a 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -34,6 +34,7 @@
#include "BKE_node.h"
#include "BKE_studiolight.h"
+#include "ED_spreadsheet.h"
#include "ED_text.h"
#include "BLI_listbase.h"
@@ -1673,7 +1674,7 @@ static void rna_SpaceImageEditor_zoom_get(PointerRNA *ptr, float *values)
values[0] = values[1] = 1;
- /* find aregion */
+ /* Find #ARegion. */
area = rna_area_from_space(ptr); /* can be NULL */
region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (region) {
@@ -1793,7 +1794,13 @@ static void rna_SpaceTextEditor_text_set(PointerRNA *ptr,
st->text = value.data;
- WM_main_add_notifier(NC_TEXT | NA_SELECTED, st->text);
+ ScrArea *area = rna_area_from_space(ptr);
+ if (area) {
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
+ if (region) {
+ ED_text_scroll_to_cursor(st, region, true);
+ }
+ }
}
static bool rna_SpaceTextEditor_text_is_syntax_highlight_supported(struct SpaceText *space)
@@ -3031,14 +3038,6 @@ static void rna_SpaceFileBrowser_browse_mode_update(Main *UNUSED(bmain),
ED_area_tag_refresh(area);
}
-static void rna_SpaceSpreadsheet_pinned_id_set(PointerRNA *ptr,
- PointerRNA value,
- struct ReportList *UNUSED(reports))
-{
- SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
- sspreadsheet->pinned_id = value.data;
-}
-
static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bmain),
Scene *UNUSED(scene),
PointerRNA *ptr)
@@ -3047,9 +3046,13 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
}
+ if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE &&
+ !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
+ }
}
-const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
+const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C),
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
@@ -3057,20 +3060,23 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
GeometryComponentType component_type = sspreadsheet->geometry_component_type;
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
- Object *active_object = CTX_data_active_object(C);
- Object *used_object = (sspreadsheet->pinned_id && GS(sspreadsheet->pinned_id->name) == ID_OB) ?
- (Object *)sspreadsheet->pinned_id :
- active_object;
- if (used_object != NULL) {
- if (used_object->type == OB_POINTCLOUD) {
- component_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
- }
- else {
- component_type = GEO_COMPONENT_TYPE_MESH;
+ ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet);
+ if (used_id != NULL) {
+ if (GS(used_id->name) == ID_OB) {
+ Object *used_object = (Object *)used_id;
+ if (used_object->type == OB_POINTCLOUD) {
+ component_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
+ }
+ else {
+ component_type = GEO_COMPONENT_TYPE_MESH;
+ }
}
}
}
+ static EnumPropertyItem mesh_vertex_domain_item = {
+ ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", "Attribute per point/vertex"};
+
EnumPropertyItem *item_array = NULL;
int items_len = 0;
for (const EnumPropertyItem *item = rna_enum_attribute_domain_items; item->identifier != NULL;
@@ -3089,7 +3095,17 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
continue;
}
}
- RNA_enum_item_add(&item_array, &items_len, item);
+ if (component_type == GEO_COMPONENT_TYPE_CURVE) {
+ if (!ELEM(item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ continue;
+ }
+ }
+ if (item->value == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH) {
+ RNA_enum_item_add(&item_array, &items_len, &mesh_vertex_domain_item);
+ }
+ else {
+ RNA_enum_item_add(&item_array, &items_len, item);
+ }
}
RNA_enum_item_end(&item_array, &items_len);
@@ -3097,6 +3113,61 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
return item_array;
}
+static SpreadsheetContext *rna_SpaceSpreadsheet_context_path_append(SpaceSpreadsheet *sspreadsheet,
+ int type)
+{
+ SpreadsheetContext *context = ED_spreadsheet_context_new(type);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+ return context;
+}
+
+static void rna_SpaceSpreadsheet_context_path_clear(SpaceSpreadsheet *sspreadsheet)
+{
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+}
+
+static StructRNA *rna_spreadsheet_context_refine(PointerRNA *ptr)
+{
+ SpreadsheetContext *context = ptr->data;
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT:
+ return &RNA_SpreadsheetContextObject;
+ case SPREADSHEET_CONTEXT_MODIFIER:
+ return &RNA_SpreadsheetContextModifier;
+ case SPREADSHEET_CONTEXT_NODE:
+ return &RNA_SpreadsheetContextNode;
+ }
+ BLI_assert_unreachable();
+ return NULL;
+}
+
+static void rna_spreadsheet_context_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ bScreen *screen = (bScreen *)ptr->owner_id;
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = area->spacedata.first;
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ }
+ }
+}
+
+static void rna_spreadsheet_set_geometry_node_context(SpaceSpreadsheet *sspreadsheet,
+ SpaceNode *snode,
+ bNode *node)
+{
+ ED_spreadsheet_set_geometry_node_context(sspreadsheet, snode, node);
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+}
+
#else
static const EnumPropertyItem dt_uv_items[] = {
@@ -3399,6 +3470,11 @@ static void rna_def_space_outliner(BlenderRNA *brna)
ICON_RNA,
"Data API",
"Display low level Blender data and its properties"},
+ {SO_OVERRIDES_LIBRARY,
+ "LIBRARY_OVERRIDES",
+ ICON_LIBRARY_DATA_OVERRIDE,
+ "Library Overrides",
+ "Display data-blocks with library overrides and list their overridden properties"},
{SO_ID_ORPHANS,
"ORPHAN_DATA",
ICON_ORPHAN_DATA,
@@ -3588,6 +3664,16 @@ static void rna_def_space_outliner(BlenderRNA *brna)
"Show Library Overrides",
"For libraries with overrides created, show the overridden values");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL);
+
+ prop = RNA_def_property(srna, "use_filter_lib_override_system", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "filter", SO_FILTER_SHOW_SYSTEM_OVERRIDES);
+ RNA_def_property_ui_text(
+ prop,
+ "Show System Overrides",
+ "For libraries with overrides created, show the overridden values that are "
+ "defined/controlled automatically (e.g. to make users of an overridden data-block point to "
+ "the override data, not the original linked data)");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL);
}
static void rna_def_space_view3d_shading(BlenderRNA *brna)
@@ -4080,7 +4166,7 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_LOOK_DEV);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "HDRI Preview", "Show HDRI preview spheres");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL);
prop = RNA_def_property(srna, "show_wireframes", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_WIREFRAMES);
@@ -4406,7 +4492,6 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
prop = RNA_def_property(srna, "gpencil_vertex_paint_opacity", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_vertex_paint_opacity");
RNA_def_property_range(prop, 0.0f, 1.0f);
- RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "Opacity", "Vertex Paint mix factor");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update");
}
@@ -5381,7 +5466,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXIES);
RNA_def_property_ui_text(
prop, "Use Proxies", "Use optimized files for faster scrubbing when available");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache");
/* grease pencil */
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
@@ -6476,6 +6561,16 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem asset_import_type_items[] = {
+ {FILE_ASSET_IMPORT_LINK, "LINK", 0, "Link", "Import the assets as linked data-block"},
+ {FILE_ASSET_IMPORT_APPEND,
+ "APPEND",
+ 0,
+ "Append",
+ "Import the assets as copied data-block, with no link to the original asset data-block"},
+ {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");
@@ -6497,6 +6592,13 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
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);
+
+ prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, asset_import_type_items);
+ RNA_def_property_ui_text(prop, "Import Type", "Determine how the asset will be imported");
+ /* Asset drag info saved by buttons stores the import type, so the space must redraw when import
+ * type changes. */
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
static void rna_def_filemenu_entry(BlenderRNA *brna)
@@ -7309,10 +7411,93 @@ static void rna_def_space_clip(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL);
}
-static void rna_def_space_spreadsheet(BlenderRNA *brna)
+static const EnumPropertyItem spreadsheet_context_type_items[] = {
+ {SPREADSHEET_CONTEXT_OBJECT, "OBJECT", ICON_NONE, "Object", ""},
+ {SPREADSHEET_CONTEXT_MODIFIER, "MODIFIER", ICON_NONE, "Modifier", ""},
+ {SPREADSHEET_CONTEXT_NODE, "NODE", ICON_NONE, "Node", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void rna_def_space_spreadsheet_context(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetContext", NULL);
+ RNA_def_struct_ui_text(srna, "Spreadsheet Context", "Element of spreadsheet context path");
+ RNA_def_struct_refine_func(srna, "rna_spreadsheet_context_refine");
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, spreadsheet_context_type_items);
+ RNA_def_property_ui_text(prop, "Type", "Type of the context");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+}
+
+static void rna_def_space_spreadsheet_context_object(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetContextObject", "SpreadsheetContext");
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update");
+}
+
+static void rna_def_space_spreadsheet_context_modifier(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetContextModifier", "SpreadsheetContext");
+
+ prop = RNA_def_property(srna, "modifier_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Modifier Name", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update");
+}
+
+static void rna_def_space_spreadsheet_context_node(BlenderRNA *brna)
{
+ StructRNA *srna;
PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetContextNode", "SpreadsheetContext");
+
+ prop = RNA_def_property(srna, "node_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Node Name", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update");
+}
+
+static void rna_def_space_spreadsheet_context_path(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *parm;
+ FunctionRNA *func;
+
+ RNA_def_property_srna(cprop, "SpreadsheetContextPath");
+ srna = RNA_def_struct(brna, "SpreadsheetContextPath", NULL);
+ RNA_def_struct_sdna(srna, "SpaceSpreadsheet");
+
+ func = RNA_def_function(srna, "append", "rna_SpaceSpreadsheet_context_path_append");
+ RNA_def_function_ui_description(func, "Append a context path element");
+ parm = RNA_def_property(func, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ RNA_def_property_enum_items(parm, spreadsheet_context_type_items);
+ parm = RNA_def_pointer(
+ func, "context", "SpreadsheetContext", "", "Newly created context path element");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "clear", "rna_SpaceSpreadsheet_context_path_clear");
+ RNA_def_function_ui_description(func, "Clear entire context path");
+}
+
+static void rna_def_space_spreadsheet(BlenderRNA *brna)
+{
+ PropertyRNA *prop, *parm;
StructRNA *srna;
+ FunctionRNA *func;
static const EnumPropertyItem geometry_component_type_items[] = {
{GEO_COMPONENT_TYPE_MESH,
@@ -7325,6 +7510,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
ICON_POINTCLOUD_DATA,
"Point Cloud",
"Point cloud component containing only point data"},
+ {GEO_COMPONENT_TYPE_CURVE,
+ "CURVE",
+ ICON_CURVE_DATA,
+ "Curve",
+ "Curve component containing spline and control point data"},
{GEO_COMPONENT_TYPE_INSTANCES,
"INSTANCES",
ICON_EMPTY_AXIS,
@@ -7334,11 +7524,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
};
static const EnumPropertyItem object_eval_state_items[] = {
- {SPREADSHEET_OBJECT_EVAL_STATE_FINAL,
- "FINAL",
+ {SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED,
+ "EVALUATED",
ICON_NONE,
- "Final",
- "Use data from object with all modifiers applied"},
+ "Evaluated",
+ "Use data from fully or partially evaluated object"},
{SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL,
"ORIGINAL",
ICON_NONE,
@@ -7347,17 +7537,31 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ rna_def_space_spreadsheet_context(brna);
+ rna_def_space_spreadsheet_context_object(brna);
+ rna_def_space_spreadsheet_context_modifier(brna);
+ rna_def_space_spreadsheet_context_node(brna);
+
srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space");
RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data");
rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_FOOTER));
- prop = RNA_def_property(srna, "pinned_id", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE);
- RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceSpreadsheet_pinned_id_set", NULL, NULL);
- RNA_def_property_ui_text(prop, "Pinned ID", "Data-block whose values are displayed");
+ prop = RNA_def_property(srna, "is_pinned", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_PINNED);
+ RNA_def_property_ui_text(prop, "Is Pinned", "Context path is pinned");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "display_context_path_collapsed", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_CONTEXT_PATH_COLLAPSED);
+ RNA_def_property_ui_text(prop, "Display Context Path Collapsed", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+ prop = RNA_def_property(srna, "context_path", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "SpreadsheetContext");
+ RNA_def_property_ui_text(prop, "Context Path", "Context path to the data being displayed");
+ rna_def_space_spreadsheet_context_path(brna, prop);
+
prop = RNA_def_property(srna, "show_only_selected", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", SPREADSHEET_FILTER_SELECTED_ONLY);
RNA_def_property_ui_text(
@@ -7382,6 +7586,16 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_enum_items(prop, object_eval_state_items);
RNA_def_property_ui_text(prop, "Object Evaluation State", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ func = RNA_def_function(
+ srna, "set_geometry_node_context", "rna_spreadsheet_set_geometry_node_context");
+ RNA_def_function_ui_description(
+ func, "Update context_path to point to a specific node in a node editor");
+ parm = RNA_def_pointer(
+ func, "node_editor", "SpaceNodeEditor", "", "Editor to take the context from");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "node", "Node", "", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
void RNA_def_space(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c
index c4a44556cf4..336359a9dc0 100644
--- a/source/blender/makesrna/intern/rna_tracking.c
+++ b/source/blender/makesrna/intern/rna_tracking.c
@@ -697,7 +697,7 @@ static MovieTrackingMarker *rna_trackingMarkers_find_frame(MovieTrackingTrack *t
static MovieTrackingMarker *rna_trackingMarkers_insert_frame(MovieTrackingTrack *track,
int framenr,
- float *co)
+ float co[2])
{
MovieTrackingMarker marker, *new_marker;
@@ -2367,6 +2367,7 @@ static void rna_def_trackingObject(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "keyframe1");
RNA_def_property_ui_text(
prop, "Keyframe A", "First keyframe used for reconstruction initialization");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
/* keyframe_b */
prop = RNA_def_property(srna, "keyframe_b", PROP_INT, PROP_NONE);
@@ -2374,6 +2375,7 @@ static void rna_def_trackingObject(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "keyframe2");
RNA_def_property_ui_text(
prop, "Keyframe B", "Second keyframe used for reconstruction initialization");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
}
static void rna_def_trackingObjects(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index aefb77c4077..8f596ec6a5c 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -599,7 +599,7 @@ static void uilist_filter_items(uiList *ui_list,
items_shown = flt_data->items_shown = shown_idx;
flt_data->items_filter_neworder = MEM_mallocN(sizeof(int) * items_shown, __func__);
/* And now, bring back new indices into the [0, items_shown[ range!
- * XXX This is O(N²)... :/
+ * XXX This is O(N^2). :/
*/
for (shown_idx = 0, prev_ni = -1; shown_idx < items_shown; shown_idx++) {
for (i = 0, t_ni = len, t_idx = -1; i < items_shown; i++) {
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 4c86405a44d..4d45d1d6d63 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -779,7 +779,7 @@ static const EnumPropertyItem *rna_userdef_audio_device_itemf(bContext *UNUSED(C
RNA_enum_item_add(&item, &totitem, &new_item);
}
-# ifndef NDEBUG
+# if !defined(NDEBUG) || !defined(WITH_AUDASPACE)
if (i == 0) {
EnumPropertyItem new_item = {i, "SOUND_NONE", 0, "No Sound", ""};
RNA_enum_item_add(&item, &totitem, &new_item);
@@ -1072,10 +1072,9 @@ static void rna_UserDef_studiolight_solid_lights_begin(CollectionPropertyIterato
rna_iterator_array_begin(iter, sl->light, sizeof(*sl->light), ARRAY_SIZE(sl->light), 0, NULL);
}
-static int rna_UserDef_studiolight_solid_lights_length(PointerRNA *ptr)
+static int rna_UserDef_studiolight_solid_lights_length(PointerRNA *UNUSED(ptr))
{
- StudioLight *sl = (StudioLight *)ptr->data;
- return ARRAY_SIZE(sl->light);
+ return ARRAY_SIZE(((StudioLight *)NULL)->light);
}
/* StudioLight.light_ambient */
@@ -6160,7 +6159,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
prop,
"Python Scripts Directory",
"Alternate script path, matching the default layout with subdirectories: "
- "startup, add-ons and modules (requires restart)");
+ "startup, addons, modules, and presets (requires restart)");
/* TODO, editing should reset sys.path! */
prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH);
@@ -6215,7 +6214,8 @@ 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 and edit mode data won't be saved)");
+ "uses process ID.\n"
+ "Warning: 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);
@@ -6275,6 +6275,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
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_full_frame_compositor", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_full_frame_compositor", 1);
+ RNA_def_property_ui_text(prop,
+ "Full Frame Compositor",
+ "Enable compositor full frame execution mode option (no tiling, "
+ "reduces execution time and memory usage)");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1);
RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui");
@@ -6288,11 +6296,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_vertex_colors", 1);
RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "Use the new Vertex Painting system");
- prop = RNA_def_property(srna, "use_switch_object_operator", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "use_switch_object_operator", 1);
- RNA_def_property_ui_text(
- prop, "Switch Object Operator", "Enable the operator to switch objects by pressing D");
-
prop = RNA_def_property(srna, "use_sculpt_tools_tilt", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_tools_tilt", 1);
RNA_def_property_ui_text(
@@ -6304,6 +6307,11 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
prop,
"Asset Browser",
"Enable Asset Browser editor and operators to manage data-blocks as asset");
+
+ prop = RNA_def_property(srna, "use_override_templates", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_override_templates", 1);
+ RNA_def_property_ui_text(
+ prop, "Override Templates", "Enable library override template in the python API");
}
static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c
index 1bddd9152db..76db6f3e325 100644
--- a/source/blender/makesrna/intern/rna_volume.c
+++ b/source/blender/makesrna/intern/rna_volume.c
@@ -137,7 +137,7 @@ static void rna_Volume_grids_end(CollectionPropertyIterator *UNUSED(iter))
static PointerRNA rna_Volume_grids_get(CollectionPropertyIterator *iter)
{
Volume *volume = iter->internal.count.ptr;
- const VolumeGrid *grid = BKE_volume_grid_get(volume, iter->internal.count.item);
+ const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, iter->internal.count.item);
return rna_pointer_inherit_refine(&iter->parent, &RNA_VolumeGrid, (void *)grid);
}
@@ -207,6 +207,16 @@ static int rna_VolumeGrids_frame_filepath_length(PointerRNA *ptr)
return strlen(BKE_volume_grids_frame_filepath(volume));
}
+static bool rna_Volume_load(Volume *volume, Main *bmain)
+{
+ return BKE_volume_load(volume, bmain);
+}
+
+static bool rna_Volume_save(Volume *volume, Main *bmain, ReportList *reports, const char *filepath)
+{
+ return BKE_volume_save(volume, bmain, reports, filepath);
+}
+
#else
static void rna_def_volume_grid(BlenderRNA *brna)
@@ -335,7 +345,7 @@ static void rna_def_volume_grids(BlenderRNA *brna, PropertyRNA *cprop)
FunctionRNA *func;
PropertyRNA *parm;
- func = RNA_def_function(srna, "load", "BKE_volume_load");
+ func = RNA_def_function(srna, "load", "rna_Volume_load");
RNA_def_function_ui_description(func, "Load list of grids and metadata from file");
RNA_def_function_flag(func, FUNC_USE_MAIN);
parm = RNA_def_boolean(func, "success", 0, "", "True if grid list was successfully loaded");
@@ -344,7 +354,7 @@ static void rna_def_volume_grids(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "unload", "BKE_volume_unload");
RNA_def_function_ui_description(func, "Unload all grid and voxel data from memory");
- func = RNA_def_function(srna, "save", "BKE_volume_save");
+ func = RNA_def_function(srna, "save", "rna_Volume_save");
RNA_def_function_ui_description(func, "Save grids and metadata to file");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_string_file_path(func, "filepath", NULL, 0, "", "File path to save to");
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index d67d62d7af9..2b1a5f7fde1 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -604,14 +604,22 @@ static PointerRNA rna_Operator_options_get(PointerRNA *ptr)
static PointerRNA rna_Operator_properties_get(PointerRNA *ptr)
{
wmOperator *op = (wmOperator *)ptr->data;
- return rna_pointer_inherit_refine(ptr, op->type->srna, op->properties);
+
+ PointerRNA result;
+ WM_operator_properties_create_ptr(&result, op->type);
+ result.data = op->properties;
+ return result;
}
static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr)
{
wmOperatorTypeMacro *otmacro = (wmOperatorTypeMacro *)ptr->data;
wmOperatorType *ot = WM_operatortype_find(otmacro->idname, true);
- return rna_pointer_inherit_refine(ptr, ot->srna, otmacro->properties);
+
+ PointerRNA result;
+ WM_operator_properties_create_ptr(&result, ot);
+ result.data = otmacro->properties;
+ return result;
}
static const EnumPropertyItem *rna_Event_value_itemf(bContext *UNUSED(C),
@@ -880,6 +888,7 @@ static PointerRNA rna_KeyMapItem_properties_get(PointerRNA *ptr)
wmKeyMapItem *kmi = ptr->data;
if (kmi->ptr) {
+ BLI_assert(kmi->ptr->owner_id == NULL);
return *(kmi->ptr);
}
@@ -1781,41 +1790,40 @@ static void rna_Operator_bl_label_set(PointerRNA *ptr, const char *value)
}
}
-static void rna_Operator_bl_translation_context_set(PointerRNA *ptr, const char *value)
-{
- wmOperator *data = (wmOperator *)(ptr->data);
- char *str = (char *)data->type->translation_context;
- if (!str[0]) {
- BLI_strncpy(str, value, RNA_DYN_DESCR_MAX); /* utf8 already ensured */
- }
- else {
- BLI_assert(!"setting the bl_translation_context on a non-builtin operator");
- }
-}
-
-static void rna_Operator_bl_description_set(PointerRNA *ptr, const char *value)
-{
- wmOperator *data = (wmOperator *)(ptr->data);
- char *str = (char *)data->type->description;
- if (!str[0]) {
- BLI_strncpy(str, value, RNA_DYN_DESCR_MAX); /* utf8 already ensured */
- }
- else {
- BLI_assert(!"setting the bl_description on a non-builtin operator");
- }
-}
+/**
+ * Use callbacks that check for NULL instead of clearing #PROP_NEVER_NULL on the string property,
+ * so the internal value may be NULL, without allowing Python to assign `None` which doesn't
+ * make any sense in this case.
+ */
+# define OPERATOR_STR_MAYBE_NULL_GETSET(attr, len) \
+ static void rna_Operator_bl_##attr##_set(PointerRNA *ptr, const char *value) \
+ { \
+ wmOperator *data = (wmOperator *)(ptr->data); \
+ char *str = (char *)data->type->attr; \
+ if (str && !str[0]) { \
+ BLI_strncpy(str, value, len); /* utf8 already ensured */ \
+ } \
+ else { \
+ BLI_assert( \
+ !"setting the bl_" STRINGIFY(translation_context) " on a non-builtin operator"); \
+ } \
+ } \
+ static void rna_Operator_bl_##attr##_get(PointerRNA *ptr, char *value) \
+ { \
+ const wmOperator *data = (wmOperator *)(ptr->data); \
+ const char *str = data->type->attr; \
+ BLI_strncpy(value, str ? str : "", len); \
+ } \
+ static int rna_Operator_bl_##attr##_length(PointerRNA *ptr) \
+ { \
+ const wmOperator *data = (wmOperator *)(ptr->data); \
+ const char *str = data->type->attr; \
+ return BLI_strnlen(str ? str : "", len); \
+ }
-static void rna_Operator_bl_undo_group_set(PointerRNA *ptr, const char *value)
-{
- wmOperator *data = (wmOperator *)(ptr->data);
- char *str = (char *)data->type->undo_group;
- if (!str[0]) {
- BLI_strncpy(str, value, OP_MAX_TYPENAME); /* utf8 already ensured */
- }
- else {
- BLI_assert(!"setting the bl_undo_group on a non-builtin operator");
- }
-}
+OPERATOR_STR_MAYBE_NULL_GETSET(translation_context, RNA_DYN_DESCR_MAX)
+OPERATOR_STR_MAYBE_NULL_GETSET(description, RNA_DYN_DESCR_MAX)
+OPERATOR_STR_MAYBE_NULL_GETSET(undo_group, OP_MAX_TYPENAME)
static void rna_KeyMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
@@ -1930,26 +1938,32 @@ static void rna_def_operator(BlenderRNA *brna)
prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->translation_context");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_translation_context_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_translation_context_get",
+ "rna_Operator_bl_translation_context_length",
+ "rna_Operator_bl_translation_context_set");
RNA_def_property_string_default(prop, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->description");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_description_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_description_get",
+ "rna_Operator_bl_description_length",
+ "rna_Operator_bl_description_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->undo_group");
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_undo_group_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_undo_group_get",
+ "rna_Operator_bl_undo_group_length",
+ "rna_Operator_bl_undo_group_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
@@ -1969,7 +1983,7 @@ static void rna_def_operator(BlenderRNA *brna)
RNA_def_struct_refine_func(srna, "rna_OperatorProperties_refine");
RNA_def_struct_idprops_func(srna, "rna_OperatorProperties_idprops");
RNA_def_struct_property_tags(srna, rna_enum_operator_property_tags);
- RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES);
+ RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES | STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID);
RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
}
@@ -2022,26 +2036,32 @@ static void rna_def_macro_operator(BlenderRNA *brna)
prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->translation_context");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_translation_context_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_translation_context_get",
+ "rna_Operator_bl_translation_context_length",
+ "rna_Operator_bl_translation_context_set");
RNA_def_property_string_default(prop, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->description");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_description_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_description_get",
+ "rna_Operator_bl_description_length",
+ "rna_Operator_bl_description_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->undo_group");
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_undo_group_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_undo_group_get",
+ "rna_Operator_bl_undo_group_length",
+ "rna_Operator_bl_undo_group_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c
index f7d139dd706..e91df38c96e 100644
--- a/source/blender/makesrna/intern/rna_wm_gizmo.c
+++ b/source/blender/makesrna/intern/rna_wm_gizmo.c
@@ -534,12 +534,16 @@ static void rna_Gizmo_unregister(struct Main *bmain, StructRNA *type)
return;
}
+ WM_gizmotype_remove_ptr(NULL, bmain, gzt);
+
+ /* Free extension after removing instances so `__del__` doesn't crash, see: T85567. */
RNA_struct_free_extension(type, &gzt->rna_ext);
RNA_struct_free(&BLENDER_RNA, type);
- WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
+ /* Free gizmo group after the extension as it owns the identifier memory. */
+ WM_gizmotype_free_ptr(gzt);
- WM_gizmotype_remove_ptr(NULL, bmain, gzt);
+ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
}
static void **rna_Gizmo_instance(PointerRNA *ptr)
@@ -934,12 +938,16 @@ static void rna_GizmoGroup_unregister(struct Main *bmain, StructRNA *type)
return;
}
+ WM_gizmo_group_type_remove_ptr(bmain, gzgt);
+
+ /* Free extension after removing instances so `__del__` doesn't crash, see: T85567. */
RNA_struct_free_extension(type, &gzgt->rna_ext);
RNA_struct_free(&BLENDER_RNA, type);
- WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
+ /* Free gizmo group after the extension as it owns the identifier memory. */
+ WM_gizmo_group_type_free_ptr(gzgt);
- WM_gizmo_group_type_remove_ptr(bmain, gzgt);
+ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
}
static void **rna_GizmoGroup_instance(PointerRNA *ptr)
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index c19782df44b..0138dd0c3ad 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -50,7 +50,7 @@ set(SRC
intern/MOD_armature.c
intern/MOD_array.c
intern/MOD_bevel.c
- intern/MOD_boolean.c
+ intern/MOD_boolean.cc
intern/MOD_build.c
intern/MOD_cast.c
intern/MOD_cloth.c
@@ -79,6 +79,7 @@ set(SRC
intern/MOD_mirror.c
intern/MOD_multires.c
intern/MOD_nodes.cc
+ intern/MOD_nodes_evaluator.cc
intern/MOD_none.c
intern/MOD_normal_edit.c
intern/MOD_ocean.c
@@ -118,6 +119,7 @@ set(SRC
MOD_modifiertypes.h
MOD_nodes.h
intern/MOD_meshcache_util.h
+ intern/MOD_nodes_evaluator.hh
intern/MOD_solidify_util.h
intern/MOD_ui_common.h
intern/MOD_util.h
@@ -181,6 +183,30 @@ endif()
if(WITH_GMP)
add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
+if(WITH_TBB)
+ add_definitions(-DWITH_TBB)
+ if(WIN32)
+ # TBB includes Windows.h which will define min/max macros
+ # that will collide with the stl versions.
+ add_definitions(-DNOMINMAX)
+ endif()
+ list(APPEND INC_SYS
+ ${TBB_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${TBB_LIBRARIES}
+ )
endif()
if(WITH_OPENVDB)
@@ -198,7 +224,7 @@ if(WITH_OPENVDB)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c
index 6769f14f88f..649d36e3d57 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -296,7 +296,6 @@ ModifierTypeInfo modifierType_Armature = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index b48bf722526..93a9e76ffe4 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -39,6 +39,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "BKE_anim_path.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
@@ -471,9 +472,9 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob != NULL) {
Object *curve_ob = amd->curve_ob;
CurveCache *curve_cache = curve_ob->runtime.curve_cache;
- if (curve_cache != NULL && curve_cache->path != NULL) {
+ if (curve_cache != NULL && curve_cache->anim_path_accum_length != NULL) {
float scale_fac = mat4_to_scale(curve_ob->obmat);
- length = scale_fac * curve_cache->path->totdist;
+ length = scale_fac * BKE_anim_path_get_length(curve_cache);
}
}
@@ -1020,7 +1021,6 @@ ModifierTypeInfo modifierType_Array = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index a94411d897e..8fdd222402e 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -448,7 +448,6 @@ ModifierTypeInfo modifierType_Bevel = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
/* freeData */ freeData,
diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c
deleted file mode 100644
index fae8ac72108..00000000000
--- a/source/blender/modifiers/intern/MOD_boolean.c
+++ /dev/null
@@ -1,967 +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) 2005 by the Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup modifiers
- */
-
-// #define DEBUG_TIME
-
-#include <stdio.h>
-
-#include "BLI_utildefines.h"
-
-#include "BLI_alloca.h"
-#include "BLI_array.h"
-#include "BLI_math_geom.h"
-#include "BLI_math_matrix.h"
-
-#include "BLT_translation.h"
-
-#include "DNA_collection_types.h"
-#include "DNA_defaults.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_screen_types.h"
-
-#include "BKE_collection.h"
-#include "BKE_context.h"
-#include "BKE_global.h" /* only to check G.debug */
-#include "BKE_lib_id.h"
-#include "BKE_lib_query.h"
-#include "BKE_material.h"
-#include "BKE_mesh.h"
-#include "BKE_mesh_boolean_convert.h"
-#include "BKE_mesh_wrapper.h"
-#include "BKE_modifier.h"
-#include "BKE_screen.h"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "RNA_access.h"
-
-#include "MOD_ui_common.h"
-#include "MOD_util.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "MEM_guardedalloc.h"
-
-#include "bmesh.h"
-#include "bmesh_tools.h"
-#include "tools/bmesh_boolean.h"
-#include "tools/bmesh_intersect.h"
-
-#ifdef DEBUG_TIME
-# include "PIL_time.h"
-# include "PIL_time_utildefines.h"
-#endif
-
-#ifdef WITH_GMP
-static const bool bypass_bmesh = true;
-#else
-static const bool bypass_bmesh = false;
-#endif
-
-static void initData(ModifierData *md)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
-
- BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(bmd, modifier));
-
- MEMCPY_STRUCT_AFTER(bmd, DNA_struct_default_get(BooleanModifierData), modifier);
-}
-
-static bool isDisabled(const struct Scene *UNUSED(scene),
- ModifierData *md,
- bool UNUSED(useRenderParams))
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
- Collection *col = bmd->collection;
-
- if (bmd->flag & eBooleanModifierFlag_Object) {
- return !bmd->object || bmd->object->type != OB_MESH;
- }
- if (bmd->flag & eBooleanModifierFlag_Collection) {
- /* The Exact solver tolerates an empty collection. */
- return !col && bmd->solver != eBooleanModifierSolver_Exact;
- }
- return false;
-}
-
-static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
-
- walk(userData, ob, (ID **)&bmd->collection, IDWALK_CB_NOP);
- walk(userData, ob, (ID **)&bmd->object, IDWALK_CB_NOP);
-}
-
-static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
- if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != NULL) {
- DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
- DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
- }
-
- Collection *col = bmd->collection;
-
- if ((bmd->flag & eBooleanModifierFlag_Collection) && col != NULL) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
- if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
- DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
- DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- /* We need own transformation as well. */
- DEG_add_modifier_to_transform_relation(ctx->node, "Boolean Modifier");
-}
-
-static Mesh *get_quick_mesh(
- Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation)
-{
- Mesh *result = NULL;
-
- if (mesh_self->totpoly == 0 || mesh_operand_ob->totpoly == 0) {
- switch (operation) {
- case eBooleanModifierOp_Intersect:
- result = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
- break;
-
- case eBooleanModifierOp_Union:
- if (mesh_self->totpoly != 0) {
- result = mesh_self;
- }
- else {
- result = (Mesh *)BKE_id_copy_ex(NULL, &mesh_operand_ob->id, NULL, LIB_ID_COPY_LOCALIZE);
-
- float imat[4][4];
- float omat[4][4];
-
- invert_m4_m4(imat, ob_self->obmat);
- mul_m4_m4m4(omat, imat, ob_operand_ob->obmat);
-
- const int mverts_len = result->totvert;
- MVert *mv = result->mvert;
-
- for (int i = 0; i < mverts_len; i++, mv++) {
- mul_m4_v3(omat, mv->co);
- }
-
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- }
-
- break;
-
- case eBooleanModifierOp_Difference:
- result = mesh_self;
- break;
- }
- }
-
- return result;
-}
-
-/* has no meaning for faces, do this so we can tell which face is which */
-#define BM_FACE_TAG BM_ELEM_DRAW
-
-/**
- * Compare selected/unselected.
- */
-static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
-{
- return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0;
-}
-
-static bool BMD_error_messages(const Object *ob, ModifierData *md, Collection *col)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
-
- bool error_returns_result = false;
-
- const bool operand_collection = (bmd->flag & eBooleanModifierFlag_Collection) != 0;
- const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
- const bool operation_intersect = bmd->operation == eBooleanModifierOp_Intersect;
-
-#ifndef WITH_GMP
- /* If compiled without GMP, return a error. */
- if (use_exact) {
- BKE_modifier_set_error(ob, md, "Compiled without GMP, using fast solver");
- error_returns_result = false;
- }
-#endif
-
- /* If intersect is selected using fast solver, return a error. */
- if (operand_collection && operation_intersect && !use_exact) {
- BKE_modifier_set_error(ob, md, "Cannot execute, intersect only available using exact solver");
- error_returns_result = true;
- }
-
- /* If the selected collection is empty and using fast solver, return a error. */
- if (operand_collection) {
- if (!use_exact && BKE_collection_is_empty(col)) {
- BKE_modifier_set_error(ob, md, "Cannot execute, fast solver and empty collection");
- error_returns_result = true;
- }
-
- /* If the selected collection contain non mesh objects, return a error. */
- if (col) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
- if (operand_ob->type != OB_MESH) {
- BKE_modifier_set_error(
- ob, md, "Cannot execute, the selected collection contains non mesh objects");
- error_returns_result = true;
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- }
-
- return error_returns_result;
-}
-
-static BMesh *BMD_mesh_bm_create(
- Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip)
-{
- BMesh *bm;
-
- *r_is_flip = (is_negative_m4(object->obmat) != is_negative_m4(operand_ob->obmat));
-
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_operand_ob);
-
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh_operand_ob,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
-
- if (UNLIKELY(*r_is_flip)) {
- const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
- BMIter iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
- }
- }
-
- BM_mesh_bm_from_me(bm,
- mesh,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
-
- return bm;
-}
-
-/* Snap entries that are near 0 or 1 or -1 to those values. */
-static void clean_obmat(float cleaned[4][4], const float mat[4][4])
-{
- const float fuzz = 1e-6f;
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- float f = mat[i][j];
- if (fabsf(f) <= fuzz) {
- f = 0.0f;
- }
- else if (fabsf(f - 1.0f) <= fuzz) {
- f = 1.0f;
- }
- else if (fabsf(f + 1.0f) <= fuzz) {
- f = -1.0f;
- }
- cleaned[i][j] = f;
- }
- }
-}
-
-static void BMD_mesh_intersection(BMesh *bm,
- ModifierData *md,
- const ModifierEvalContext *ctx,
- Mesh *mesh_operand_ob,
- Object *object,
- Object *operand_ob,
- bool is_flip)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
-
- /* main bmesh intersection setup */
- /* create tessface & intersect */
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
- BMLoop *(*looptris)[3];
-
-#ifdef WITH_GMP
- const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
- const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
-#else
- const bool use_exact = false;
- const bool use_self = false;
-#endif
-
- looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
-
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
-
- /* postpone this until after tessellating
- * so we can use the original normals before the vertex are moved */
- {
- BMIter iter;
- int i;
- const int i_verts_end = mesh_operand_ob->totvert;
- const int i_faces_end = mesh_operand_ob->totpoly;
-
- float imat[4][4];
- float omat[4][4];
-
- if (use_exact) {
- /* The user-expected coplanar faces will actually be coplanar more
- * often if use an object matrix that doesn't multiply by values
- * other than 0, -1, or 1 in the scaling part of the matrix.
- */
- float cleaned_object_obmat[4][4];
- float cleaned_operand_obmat[4][4];
- clean_obmat(cleaned_object_obmat, object->obmat);
- invert_m4_m4(imat, cleaned_object_obmat);
- clean_obmat(cleaned_operand_obmat, operand_ob->obmat);
- mul_m4_m4m4(omat, imat, cleaned_operand_obmat);
- }
- else {
- invert_m4_m4(imat, object->obmat);
- mul_m4_m4m4(omat, imat, operand_ob->obmat);
- }
-
- BMVert *eve;
- i = 0;
- BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
- mul_m4_v3(omat, eve->co);
- if (++i == i_verts_end) {
- break;
- }
- }
-
- /* we need face normals because of 'BM_face_split_edgenet'
- * we could calculate on the fly too (before calling split). */
- {
- float nmat[3][3];
- copy_m3_m4(nmat, omat);
- invert_m3(nmat);
-
- if (UNLIKELY(is_flip)) {
- negate_m3(nmat);
- }
-
- const short ob_src_totcol = operand_ob->totcol;
- short *material_remap = BLI_array_alloca(material_remap, ob_src_totcol ? ob_src_totcol : 1);
-
- /* Using original (not evaluated) object here since we are writing to it. */
- /* XXX Pretty sure comment above is fully wrong now with CoW & co ? */
- BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap);
-
- BMFace *efa;
- i = 0;
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- mul_transposed_m3_v3(nmat, efa->no);
- normalize_v3(efa->no);
-
- /* Temp tag to test which side split faces are from. */
- BM_elem_flag_enable(efa, BM_FACE_TAG);
-
- /* remap material */
- if (LIKELY(efa->mat_nr < ob_src_totcol)) {
- efa->mat_nr = material_remap[efa->mat_nr];
- }
-
- if (++i == i_faces_end) {
- break;
- }
- }
- }
- }
-
- /* not needed, but normals for 'dm' will be invalid,
- * currently this is ok for 'BM_mesh_intersect' */
- // BM_mesh_normals_update(bm);
-
- bool use_separate = false;
- bool use_dissolve = true;
- bool use_island_connect = true;
-
- /* change for testing */
- if (G.debug & G_DEBUG) {
- use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0;
- use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0;
- use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) == 0;
- }
-
- if (use_exact) {
- BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, NULL, 2, use_self, false, false, bmd->operation);
- }
- else {
- BM_mesh_intersect(bm,
- looptris,
- tottri,
- bm_face_isect_pair,
- NULL,
- false,
- use_separate,
- use_dissolve,
- use_island_connect,
- false,
- false,
- bmd->operation,
- bmd->double_threshold);
- }
- MEM_freeN(looptris);
-}
-
-static int bm_face_isect_nary(BMFace *f, void *user_data)
-{
- int *shape = (int *)user_data;
- return shape[BM_elem_index_get(f)];
-}
-
-/* The Exact solver can do all operands of a collection at once. */
-static Mesh *collection_boolean_exact(BooleanModifierData *bmd,
- const ModifierEvalContext *ctx,
- Mesh *mesh)
-{
- int i;
- Mesh *result = mesh;
- Collection *col = bmd->collection;
- int num_shapes = 1;
- Mesh **meshes = NULL;
- Object **objects = NULL;
- BLI_array_declare(meshes);
- BLI_array_declare(objects);
- BMAllocTemplate bat;
- bat.totvert = mesh->totvert;
- bat.totedge = mesh->totedge;
- bat.totloop = mesh->totloop;
- bat.totface = mesh->totpoly;
- BLI_array_append(meshes, mesh);
- BLI_array_append(objects, ctx->object);
- Mesh *col_mesh;
- /* Allow col to be empty: then target mesh will just remove self-intersections. */
- if (col) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, ob) {
- if (ob->type == OB_MESH && ob != ctx->object) {
- col_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob, false);
- /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
- * But for 2.90 better not try to be smart here. */
- BKE_mesh_wrapper_ensure_mdata(col_mesh);
- BLI_array_append(meshes, col_mesh);
- BLI_array_append(objects, ob);
- bat.totvert += col_mesh->totvert;
- bat.totedge += col_mesh->totedge;
- bat.totloop += col_mesh->totloop;
- bat.totface += col_mesh->totpoly;
- ++num_shapes;
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- int *shape_face_end = MEM_mallocN(num_shapes * sizeof(int), __func__);
- int *shape_vert_end = MEM_mallocN(num_shapes * sizeof(int), __func__);
- bool is_neg_mat0 = is_negative_m4(ctx->object->obmat);
- BMesh *bm = BM_mesh_create(&bat,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
- for (i = 0; i < num_shapes; i++) {
- Mesh *me = meshes[i];
- Object *ob = objects[i];
- /* Need normals for triangulation. */
- BM_mesh_bm_from_me(bm,
- me,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
- shape_face_end[i] = me->totpoly + (i == 0 ? 0 : shape_face_end[i - 1]);
- shape_vert_end[i] = me->totvert + (i == 0 ? 0 : shape_vert_end[i - 1]);
- if (i > 0) {
- bool is_flip = (is_neg_mat0 != is_negative_m4(ob->obmat));
- if (UNLIKELY(is_flip)) {
- const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
- BMIter iter;
- BMFace *efa;
- BM_mesh_elem_index_ensure(bm, BM_FACE);
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_index_get(efa) >= shape_face_end[i - 1]) {
- BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
- }
- }
- }
- }
- }
-
- /* Triangulate the mesh. */
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
- BMLoop *(*looptris)[3];
- looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
-
- /* Move the vertices of all but the first shape into transformation space of first mesh.
- * Do this after tesselation so don't need to recalculate normals.
- * The Exact solver doesn't need normals on the input faces. */
- float imat[4][4];
- float omat[4][4];
- float cleaned_object_obmat[4][4];
- clean_obmat(cleaned_object_obmat, ctx->object->obmat);
- invert_m4_m4(imat, cleaned_object_obmat);
- int curshape = 0;
- int curshape_vert_end = shape_vert_end[0];
- BMVert *eve;
- BMIter iter;
- i = 0;
- BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
- if (i == curshape_vert_end) {
- curshape++;
- curshape_vert_end = shape_vert_end[curshape];
- clean_obmat(cleaned_object_obmat, objects[curshape]->obmat);
- mul_m4_m4m4(omat, imat, cleaned_object_obmat);
- }
- if (curshape > 0) {
- mul_m4_v3(omat, eve->co);
- }
- i++;
- }
-
- /* Remap the materials. Fill a shape array for test function. Calculate normals. */
- int *shape = MEM_mallocN(bm->totface * sizeof(int), __func__);
- curshape = 0;
- int curshape_face_end = shape_face_end[0];
- int curshape_ncol = ctx->object->totcol;
- short *material_remap = NULL;
- BMFace *efa;
- i = 0;
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- if (i == curshape_face_end) {
- curshape++;
- curshape_face_end = shape_face_end[curshape];
- if (material_remap != NULL) {
- MEM_freeN(material_remap);
- }
- curshape_ncol = objects[curshape]->totcol;
- material_remap = MEM_mallocN(curshape_ncol ? curshape_ncol : 1, __func__);
- BKE_object_material_remap_calc(ctx->object, objects[curshape], material_remap);
- }
- shape[i] = curshape;
- if (curshape > 0) {
- /* Normals for other shapes changed because vertex positions changed.
- * Boolean doesn't need these, but post-boolean code (interpolation) does. */
- BM_face_normal_update(efa);
- if (LIKELY(efa->mat_nr < curshape_ncol)) {
- efa->mat_nr = material_remap[efa->mat_nr];
- }
- }
- i++;
- }
-
- BM_mesh_elem_index_ensure(bm, BM_FACE);
- BM_mesh_boolean(bm,
- looptris,
- tottri,
- bm_face_isect_nary,
- shape,
- num_shapes,
- true,
- false,
- false,
- bmd->operation);
-
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
-
- MEM_freeN(shape);
- MEM_freeN(shape_face_end);
- MEM_freeN(shape_vert_end);
- MEM_freeN(looptris);
- if (material_remap != NULL) {
- MEM_freeN(material_remap);
- }
- BLI_array_free(meshes);
- BLI_array_free(objects);
- return result;
-}
-
-#ifdef WITH_GMP
-
-/* Get a mapping from material slot numbers in the src_ob to slot numbers in the dst_ob.
- * If a material doesn't exist in the dst_ob, the mapping just goes to the same slot
- * or to zero if there aren't enough slots in the destination.
- * Caller must MEM_freeN the returned array. */
-static short *get_material_remap(Object *dest_ob, Object *src_ob)
-{
- short *remap;
- int n = dest_ob->totcol;
- if (n <= 0) {
- n = 1;
- }
- remap = MEM_mallocN(n * sizeof(short), __func__);
- BKE_object_material_remap_calc(dest_ob, src_ob, remap);
- return remap;
-}
-
-/* New method: bypass trip through BMesh. */
-static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
- const ModifierEvalContext *ctx,
- Mesh *mesh)
-{
- Mesh *result;
- Mesh *mesh_operand;
- short *remap;
- Mesh **meshes = NULL;
- const float(**obmats)[4][4] = NULL;
- short **material_remaps = NULL;
- BLI_array_declare(meshes);
- BLI_array_declare(obmats);
- BLI_array_declare(material_remaps);
-
-# ifdef DEBUG_TIME
- TIMEIT_START(boolean_bmesh);
-# endif
-
- if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object == NULL) {
- return mesh;
- }
-
- BLI_array_append(meshes, mesh);
- BLI_array_append(obmats, &ctx->object->obmat);
- BLI_array_append(material_remaps, NULL);
- if (bmd->flag & eBooleanModifierFlag_Object) {
- mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object, false);
- BKE_mesh_wrapper_ensure_mdata(mesh_operand);
- BLI_array_append(meshes, mesh_operand);
- BLI_array_append(obmats, &bmd->object->obmat);
- remap = get_material_remap(ctx->object, bmd->object);
- BLI_array_append(material_remaps, remap);
- }
- else if (bmd->flag & eBooleanModifierFlag_Collection) {
- Collection *collection = bmd->collection;
- /* Allow collection to be empty: then target mesh will just removed self-intersections. */
- if (collection) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
- if (ob->type == OB_MESH && ob != ctx->object) {
- Mesh *collection_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob, false);
- BKE_mesh_wrapper_ensure_mdata(collection_mesh);
- BLI_array_append(meshes, collection_mesh);
- BLI_array_append(obmats, &ob->obmat);
- remap = get_material_remap(ctx->object, ob);
- BLI_array_append(material_remaps, remap);
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- }
-
- const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
- const bool hole_tolerant = (bmd->flag & eBooleanModifierFlag_HoleTolerant) != 0;
- result = BKE_mesh_boolean((const Mesh **)meshes,
- (const float(**)[4][4])obmats,
- (const short **)material_remaps,
- BLI_array_len(meshes),
- use_self,
- hole_tolerant,
- bmd->operation);
-
- BLI_array_free(meshes);
- BLI_array_free(obmats);
- for (int i = 0; i < BLI_array_len(material_remaps); i++) {
- remap = material_remaps[i];
- if (remap) {
- MEM_freeN(remap);
- }
- }
- BLI_array_free(material_remaps);
-
-# ifdef DEBUG_TIME
- TIMEIT_END(boolean_bmesh);
-# endif
-
- return result;
-}
-#endif
-
-static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
- Object *object = ctx->object;
- Mesh *result = mesh;
- Mesh *mesh_operand_ob;
- BMesh *bm;
- Collection *collection = bmd->collection;
-
- bool is_flip = false;
- const bool confirm_return = true;
-#ifdef WITH_GMP
- const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
- if (use_exact && bypass_bmesh) {
- return exact_boolean_mesh(bmd, ctx, mesh);
- }
-#else
- UNUSED_VARS(bypass_bmesh);
- const bool use_exact = false;
-#endif
-
-#ifdef DEBUG_TIME
- TIMEIT_START(boolean_bmesh);
-#endif
-
- if (bmd->flag & eBooleanModifierFlag_Object) {
- if (bmd->object == NULL) {
- return result;
- }
-
- BMD_error_messages(ctx->object, md, NULL);
-
- Object *operand_ob = bmd->object;
-
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_INIT(operand_get_evaluated_mesh);
- TIMEIT_BLOCK_START(operand_get_evaluated_mesh);
-#endif
- mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob, false);
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_END(operand_get_evaluated_mesh);
- TIMEIT_BLOCK_STATS(operand_get_evaluated_mesh);
-#endif
-
- if (mesh_operand_ob) {
- /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
- * But for 2.90 better not try to be smart here. */
- BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
- /* when one of objects is empty (has got no faces) we could speed up
- * calculation a bit returning one of objects' derived meshes (or empty one)
- * Returning mesh is depended on modifiers operation (sergey) */
- result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation);
-
- if (result == NULL) {
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_INIT(object_BMD_mesh_bm_create);
- TIMEIT_BLOCK_START(object_BMD_mesh_bm_create);
-#endif
- bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_END(object_BMD_mesh_bm_create);
- TIMEIT_BLOCK_STATS(object_BMD_mesh_bm_create);
-#endif
-
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_INIT(BMD_mesh_intersection);
- TIMEIT_BLOCK_START(BMD_mesh_intersection);
-#endif
- BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_END(BMD_mesh_intersection);
- TIMEIT_BLOCK_STATS(BMD_mesh_intersection);
-#endif
-
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_INIT(BKE_mesh_from_bmesh_for_eval_nomain);
- TIMEIT_BLOCK_START(BKE_mesh_from_bmesh_for_eval_nomain);
-#endif
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_END(BKE_mesh_from_bmesh_for_eval_nomain);
- TIMEIT_BLOCK_STATS(BKE_mesh_from_bmesh_for_eval_nomain);
-#endif
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- }
-
- /* if new mesh returned, return it; otherwise there was
- * an error, so delete the modifier object */
- if (result == NULL) {
- BKE_modifier_set_error(object, md, "Cannot execute boolean operation");
- }
- }
- }
-
- else {
- if (collection == NULL && !use_exact) {
- return result;
- }
-
- /* Return result for certain errors. */
- if (BMD_error_messages(ctx->object, md, collection) == confirm_return) {
- return result;
- }
-
- if (use_exact) {
- result = collection_boolean_exact(bmd, ctx, mesh);
- }
- else {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, operand_ob) {
- if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
-
- mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
- false);
-
- if (mesh_operand_ob) {
- /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
- * But for 2.90 better not try to be smart here. */
- BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
-
- bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
-
- BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
-
- /* Needed for multiple objects to work. */
- BM_mesh_bm_to_me(NULL,
- bm,
- mesh,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }));
-
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- }
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- }
-
-#ifdef DEBUG_TIME
- TIMEIT_END(boolean_bmesh);
-#endif
-
- return result;
-}
-
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
- CustomData_MeshMasks *r_cddata_masks)
-{
- r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
- r_cddata_masks->emask |= CD_MASK_MEDGE;
- r_cddata_masks->fmask |= CD_MASK_MTFACE;
-}
-
-static void panel_draw(const bContext *UNUSED(C), Panel *panel)
-{
- uiLayout *layout = panel->layout;
-
- PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
-
- uiItemR(layout, ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
-
- uiLayoutSetPropSep(layout, true);
-
- uiItemR(layout, ptr, "operand_type", 0, NULL, ICON_NONE);
-
- const bool operand_object = RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object;
-
- if (operand_object) {
- uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE);
- }
- else {
- uiItemR(layout, ptr, "collection", 0, NULL, ICON_NONE);
- }
-
- uiItemR(layout, ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
-
- modifier_panel_end(layout, ptr);
-}
-
-static void solver_options_panel_draw(const bContext *UNUSED(C), Panel *panel)
-{
- uiLayout *layout = panel->layout;
-
- PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
-
- const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Exact;
- const bool operand_object = RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object;
-
- uiLayoutSetPropSep(layout, true);
-
- uiLayout *col = uiLayoutColumn(layout, true);
- if (use_exact) {
- /* When operand is collection, we always use_self. */
- if (operand_object) {
- uiItemR(col, ptr, "use_self", 0, NULL, ICON_NONE);
- }
- uiItemR(col, ptr, "use_hole_tolerant", 0, NULL, ICON_NONE);
- }
- else {
- uiItemR(col, ptr, "double_threshold", 0, NULL, ICON_NONE);
- }
-
- if (G.debug) {
- uiItemR(col, ptr, "debug_options", 0, NULL, ICON_NONE);
- }
-}
-
-static void panelRegister(ARegionType *region_type)
-{
- PanelType *panel = modifier_panel_register(region_type, eModifierType_Boolean, panel_draw);
- modifier_subpanel_register(
- region_type, "solver_options", "Solver Options", NULL, solver_options_panel_draw, panel);
-}
-
-ModifierTypeInfo modifierType_Boolean = {
- /* name */ "Boolean",
- /* structName */ "BooleanModifierData",
- /* structSize */ sizeof(BooleanModifierData),
- /* srna */ &RNA_BooleanModifier,
- /* type */ eModifierTypeType_Nonconstructive,
- /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode,
- /* icon */ ICON_MOD_BOOLEAN,
-
- /* copyData */ BKE_modifier_copydata_generic,
-
- /* deformVerts */ NULL,
- /* deformMatrices */ NULL,
- /* deformVertsEM */ NULL,
- /* deformMatricesEM */ NULL,
- /* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
- /* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
-
- /* initData */ initData,
- /* requiredDataMask */ requiredDataMask,
- /* freeData */ NULL,
- /* isDisabled */ isDisabled,
- /* updateDepsgraph */ updateDepsgraph,
- /* dependsOnTime */ NULL,
- /* dependsOnNormals */ NULL,
- /* foreachIDLink */ foreachIDLink,
- /* foreachTexLink */ NULL,
- /* freeRuntimeData */ NULL,
- /* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
-};
diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc
new file mode 100644
index 00000000000..4b9b24e4e47
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_boolean.cc
@@ -0,0 +1,651 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 by the Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <cstdio>
+
+#include "BLI_utildefines.h"
+
+#include "BLI_array.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_math_geom.h"
+#include "BLI_math_matrix.h"
+#include "BLI_vector.hh"
+
+#include "BLT_translation.h"
+
+#include "DNA_collection_types.h"
+#include "DNA_defaults.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_collection.h"
+#include "BKE_context.h"
+#include "BKE_global.h" /* only to check G.debug */
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_boolean_convert.hh"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "MOD_ui_common.h"
+#include "MOD_util.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+#include "tools/bmesh_boolean.h"
+#include "tools/bmesh_intersect.h"
+
+// #define DEBUG_TIME
+
+#ifdef DEBUG_TIME
+# include "BLI_timeit.hh"
+#endif
+
+using blender::Array;
+using blender::float4x4;
+using blender::Vector;
+
+static void initData(ModifierData *md)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(bmd, modifier));
+
+ MEMCPY_STRUCT_AFTER(bmd, DNA_struct_default_get(BooleanModifierData), modifier);
+}
+
+static bool isDisabled(const struct Scene *UNUSED(scene),
+ ModifierData *md,
+ bool UNUSED(useRenderParams))
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ Collection *col = bmd->collection;
+
+ if (bmd->flag & eBooleanModifierFlag_Object) {
+ return !bmd->object || bmd->object->type != OB_MESH;
+ }
+ if (bmd->flag & eBooleanModifierFlag_Collection) {
+ /* The Exact solver tolerates an empty collection. */
+ return !col && bmd->solver != eBooleanModifierSolver_Exact;
+ }
+ return false;
+}
+
+static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+
+ walk(userData, ob, (ID **)&bmd->collection, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&bmd->object, IDWALK_CB_NOP);
+}
+
+static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != nullptr) {
+ DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
+ DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
+ }
+
+ Collection *col = bmd->collection;
+
+ if ((bmd->flag & eBooleanModifierFlag_Collection) && col != nullptr) {
+ DEG_add_collection_geometry_relation(ctx->node, col, "Boolean Modifier");
+ }
+ /* We need own transformation as well. */
+ DEG_add_modifier_to_transform_relation(ctx->node, "Boolean Modifier");
+}
+
+static Mesh *get_quick_mesh(
+ Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation)
+{
+ Mesh *result = nullptr;
+
+ if (mesh_self->totpoly == 0 || mesh_operand_ob->totpoly == 0) {
+ switch (operation) {
+ case eBooleanModifierOp_Intersect:
+ result = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
+ break;
+
+ case eBooleanModifierOp_Union:
+ if (mesh_self->totpoly != 0) {
+ result = mesh_self;
+ }
+ else {
+ result = (Mesh *)BKE_id_copy_ex(
+ nullptr, &mesh_operand_ob->id, nullptr, LIB_ID_COPY_LOCALIZE);
+
+ float imat[4][4];
+ float omat[4][4];
+ invert_m4_m4(imat, ob_self->obmat);
+ mul_m4_m4m4(omat, imat, ob_operand_ob->obmat);
+
+ const int mverts_len = result->totvert;
+ MVert *mv = result->mvert;
+
+ for (int i = 0; i < mverts_len; i++, mv++) {
+ mul_m4_v3(omat, mv->co);
+ }
+
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+
+ break;
+
+ case eBooleanModifierOp_Difference:
+ result = mesh_self;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/* has no meaning for faces, do this so we can tell which face is which */
+#define BM_FACE_TAG BM_ELEM_DRAW
+
+/**
+ * Compare selected/unselected.
+ */
+static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
+{
+ return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0;
+}
+
+static bool BMD_error_messages(const Object *ob, ModifierData *md)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ Collection *col = bmd->collection;
+
+ bool error_returns_result = false;
+
+ const bool operand_collection = (bmd->flag & eBooleanModifierFlag_Collection) != 0;
+ const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
+ const bool operation_intersect = bmd->operation == eBooleanModifierOp_Intersect;
+
+#ifndef WITH_GMP
+ /* If compiled without GMP, return a error. */
+ if (use_exact) {
+ BKE_modifier_set_error(ob, md, "Compiled without GMP, using fast solver");
+ error_returns_result = false;
+ }
+#endif
+
+ /* If intersect is selected using fast solver, return a error. */
+ if (operand_collection && operation_intersect && !use_exact) {
+ BKE_modifier_set_error(ob, md, "Cannot execute, intersect only available using exact solver");
+ error_returns_result = true;
+ }
+
+ /* If the selected collection is empty and using fast solver, return a error. */
+ if (operand_collection) {
+ if (!use_exact && BKE_collection_is_empty(col)) {
+ BKE_modifier_set_error(ob, md, "Cannot execute, fast solver and empty collection");
+ error_returns_result = true;
+ }
+
+ /* If the selected collection contain non mesh objects, return a error. */
+ if (col) {
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
+ if (operand_ob->type != OB_MESH) {
+ BKE_modifier_set_error(
+ ob, md, "Cannot execute, the selected collection contains non mesh objects");
+ error_returns_result = true;
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
+ }
+
+ return error_returns_result;
+}
+
+static BMesh *BMD_mesh_bm_create(
+ Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip)
+{
+#ifdef DEBUG_TIME
+ SCOPED_TIMER(__func__)
+#endif
+
+ *r_is_flip = (is_negative_m4(object->obmat) != is_negative_m4(operand_ob->obmat));
+
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_operand_ob);
+
+ BMeshCreateParams bmcp = {false};
+ BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
+
+ BMeshFromMeshParams params{};
+ params.calc_face_normal = true;
+ BM_mesh_bm_from_me(bm, mesh_operand_ob, &params);
+
+ if (UNLIKELY(*r_is_flip)) {
+ const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
+ BMIter iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
+ }
+ }
+
+ BM_mesh_bm_from_me(bm, mesh, &params);
+
+ return bm;
+}
+
+static void BMD_mesh_intersection(BMesh *bm,
+ ModifierData *md,
+ const ModifierEvalContext *ctx,
+ Mesh *mesh_operand_ob,
+ Object *object,
+ Object *operand_ob,
+ bool is_flip)
+{
+#ifdef DEBUG_TIME
+ SCOPED_TIMER(__func__)
+#endif
+
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+
+ /* main bmesh intersection setup */
+ /* create tessface & intersect */
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+ BMLoop *(*looptris)[3] = (BMLoop * (*)[3])
+ MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
+
+ BM_mesh_calc_tessellation_beauty(bm, looptris);
+
+ /* postpone this until after tessellating
+ * so we can use the original normals before the vertex are moved */
+ {
+ BMIter iter;
+ int i;
+ const int i_verts_end = mesh_operand_ob->totvert;
+ const int i_faces_end = mesh_operand_ob->totpoly;
+
+ float imat[4][4];
+ float omat[4][4];
+ invert_m4_m4(imat, object->obmat);
+ mul_m4_m4m4(omat, imat, operand_ob->obmat);
+
+ BMVert *eve;
+ i = 0;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ mul_m4_v3(omat, eve->co);
+ if (++i == i_verts_end) {
+ break;
+ }
+ }
+
+ /* we need face normals because of 'BM_face_split_edgenet'
+ * we could calculate on the fly too (before calling split). */
+ float nmat[3][3];
+ copy_m3_m4(nmat, omat);
+ invert_m3(nmat);
+
+ if (UNLIKELY(is_flip)) {
+ negate_m3(nmat);
+ }
+
+ Array<short> material_remap(operand_ob->totcol ? operand_ob->totcol : 1);
+
+ /* Using original (not evaluated) object here since we are writing to it. */
+ /* XXX Pretty sure comment above is fully wrong now with CoW & co ? */
+ BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap.data());
+
+ BMFace *efa;
+ i = 0;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ mul_transposed_m3_v3(nmat, efa->no);
+ normalize_v3(efa->no);
+
+ /* Temp tag to test which side split faces are from. */
+ BM_elem_flag_enable(efa, BM_FACE_TAG);
+
+ /* remap material */
+ if (LIKELY(efa->mat_nr < operand_ob->totcol)) {
+ efa->mat_nr = material_remap[efa->mat_nr];
+ }
+
+ if (++i == i_faces_end) {
+ break;
+ }
+ }
+ }
+
+ /* not needed, but normals for 'dm' will be invalid,
+ * currently this is ok for 'BM_mesh_intersect' */
+ // BM_mesh_normals_update(bm);
+
+ bool use_separate = false;
+ bool use_dissolve = true;
+ bool use_island_connect = true;
+
+ /* change for testing */
+ if (G.debug & G_DEBUG) {
+ use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0;
+ use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0;
+ use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) == 0;
+ }
+
+ BM_mesh_intersect(bm,
+ looptris,
+ looptris_tot,
+ bm_face_isect_pair,
+ nullptr,
+ false,
+ use_separate,
+ use_dissolve,
+ use_island_connect,
+ false,
+ false,
+ bmd->operation,
+ bmd->double_threshold);
+
+ MEM_freeN(looptris);
+}
+
+#ifdef WITH_GMP
+
+/* Get a mapping from material slot numbers in the src_ob to slot numbers in the dst_ob.
+ * If a material doesn't exist in the dst_ob, the mapping just goes to the same slot
+ * or to zero if there aren't enough slots in the destination.
+ * Caller owns the returned array. */
+static Array<short> get_material_remap(Object *dest_ob, Object *src_ob)
+{
+ int n = dest_ob->totcol;
+ if (n <= 0) {
+ n = 1;
+ }
+ Array<short> remap(n);
+ BKE_object_material_remap_calc(dest_ob, src_ob, remap.data());
+ return remap;
+}
+
+static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
+ const ModifierEvalContext *ctx,
+ Mesh *mesh)
+{
+ Vector<const Mesh *> meshes;
+ Vector<float4x4 *> obmats;
+ Vector<Array<short>> material_remaps;
+
+# ifdef DEBUG_TIME
+ SCOPED_TIMER(__func__)
+# endif
+
+ if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object == nullptr) {
+ return mesh;
+ }
+
+ meshes.append(mesh);
+ obmats.append((float4x4 *)&ctx->object->obmat);
+ material_remaps.append({});
+ if (bmd->flag & eBooleanModifierFlag_Object) {
+ Mesh *mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object, false);
+ if (!mesh_operand) {
+ return mesh;
+ }
+ BKE_mesh_wrapper_ensure_mdata(mesh_operand);
+ meshes.append(mesh_operand);
+ obmats.append((float4x4 *)&bmd->object->obmat);
+ material_remaps.append(get_material_remap(ctx->object, bmd->object));
+ }
+ else if (bmd->flag & eBooleanModifierFlag_Collection) {
+ Collection *collection = bmd->collection;
+ /* Allow collection to be empty; then target mesh will just removed self-intersections. */
+ if (collection) {
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
+ if (ob->type == OB_MESH && ob != ctx->object) {
+ Mesh *collection_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob, false);
+ if (!collection_mesh) {
+ continue;
+ }
+ BKE_mesh_wrapper_ensure_mdata(collection_mesh);
+ meshes.append(collection_mesh);
+ obmats.append((float4x4 *)&ob->obmat);
+ material_remaps.append(get_material_remap(ctx->object, ob));
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
+ }
+
+ const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
+ const bool hole_tolerant = (bmd->flag & eBooleanModifierFlag_HoleTolerant) != 0;
+ return blender::meshintersect::direct_mesh_boolean(meshes,
+ obmats,
+ *(float4x4 *)&ctx->object->obmat,
+ material_remaps,
+ use_self,
+ hole_tolerant,
+ bmd->operation);
+}
+#endif
+
+static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ Object *object = ctx->object;
+ Mesh *result = mesh;
+ Collection *collection = bmd->collection;
+
+ /* Return result for certain errors. */
+ if (BMD_error_messages(ctx->object, md)) {
+ return result;
+ }
+
+#ifdef WITH_GMP
+ if (bmd->solver == eBooleanModifierSolver_Exact) {
+ return exact_boolean_mesh(bmd, ctx, mesh);
+ }
+#endif
+
+#ifdef DEBUG_TIME
+ SCOPED_TIMER(__func__)
+#endif
+
+ if (bmd->flag & eBooleanModifierFlag_Object) {
+ if (bmd->object == nullptr) {
+ return result;
+ }
+
+ Object *operand_ob = bmd->object;
+
+ Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
+ false);
+
+ if (mesh_operand_ob) {
+ /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
+ * But for 2.90 better not try to be smart here. */
+ BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
+ /* when one of objects is empty (has got no faces) we could speed up
+ * calculation a bit returning one of objects' derived meshes (or empty one)
+ * Returning mesh is depended on modifiers operation (sergey) */
+ result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation);
+
+ if (result == nullptr) {
+ bool is_flip;
+ BMesh *bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
+
+ BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
+
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
+
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+
+ if (result == nullptr) {
+ BKE_modifier_set_error(object, md, "Cannot execute boolean operation");
+ }
+ }
+ }
+ else {
+ if (collection == nullptr) {
+ return result;
+ }
+
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, operand_ob) {
+ if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
+ Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
+ false);
+
+ if (mesh_operand_ob) {
+ /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
+ * But for 2.90 better not try to be smart here. */
+ BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
+
+ bool is_flip;
+ BMesh *bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
+
+ BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
+
+ /* Needed for multiple objects to work. */
+ BMeshToMeshParams params{};
+ params.calc_object_remap = false;
+ BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
+
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
+
+ return result;
+}
+
+static void requiredDataMask(Object *UNUSED(ob),
+ ModifierData *UNUSED(md),
+ CustomData_MeshMasks *r_cddata_masks)
+{
+ r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
+ r_cddata_masks->emask |= CD_MASK_MEDGE;
+ r_cddata_masks->fmask |= CD_MASK_MTFACE;
+}
+
+static void panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
+
+ uiItemR(layout, ptr, "operation", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "operand_type", 0, nullptr, ICON_NONE);
+ if (RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object) {
+ uiItemR(layout, ptr, "object", 0, nullptr, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, ptr, "collection", 0, nullptr, ICON_NONE);
+ }
+
+ uiItemR(layout, ptr, "solver", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+
+ modifier_panel_end(layout, ptr);
+}
+
+static void solver_options_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
+
+ const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Exact;
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayout *col = uiLayoutColumn(layout, true);
+ if (use_exact) {
+ /* When operand is collection, we always use_self. */
+ if (RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object) {
+ uiItemR(col, ptr, "use_self", 0, nullptr, ICON_NONE);
+ }
+ uiItemR(col, ptr, "use_hole_tolerant", 0, nullptr, ICON_NONE);
+ }
+ else {
+ uiItemR(col, ptr, "double_threshold", 0, nullptr, ICON_NONE);
+ }
+
+ if (G.debug) {
+ uiItemR(col, ptr, "debug_options", 0, nullptr, ICON_NONE);
+ }
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel = modifier_panel_register(region_type, eModifierType_Boolean, panel_draw);
+ modifier_subpanel_register(
+ region_type, "solver_options", "Solver Options", nullptr, solver_options_panel_draw, panel);
+}
+
+ModifierTypeInfo modifierType_Boolean = {
+ /* name */ "Boolean",
+ /* structName */ "BooleanModifierData",
+ /* structSize */ sizeof(BooleanModifierData),
+ /* srna */ &RNA_BooleanModifier,
+ /* type */ eModifierTypeType_Nonconstructive,
+ /* flags */
+ (ModifierTypeFlag)(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode),
+ /* icon */ ICON_MOD_BOOLEAN,
+
+ /* copyData */ BKE_modifier_copydata_generic,
+
+ /* deformVerts */ nullptr,
+ /* deformMatrices */ nullptr,
+ /* deformVertsEM */ nullptr,
+ /* deformMatricesEM */ nullptr,
+ /* modifyMesh */ modifyMesh,
+ /* modifyHair */ nullptr,
+ /* modifyGeometrySet */ nullptr,
+
+ /* initData */ initData,
+ /* requiredDataMask */ requiredDataMask,
+ /* freeData */ nullptr,
+ /* isDisabled */ isDisabled,
+ /* updateDepsgraph */ updateDepsgraph,
+ /* dependsOnTime */ nullptr,
+ /* dependsOnNormals */ nullptr,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ nullptr,
+ /* freeRuntimeData */ nullptr,
+ /* panelRegister */ panelRegister,
+ /* blendWrite */ nullptr,
+ /* blendRead */ nullptr,
+};
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 0b1c661baed..c38e5126f6b 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -347,7 +347,6 @@ ModifierTypeInfo modifierType_Build = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index f905a38ae12..715bc26e5d3 100644
--- a/source/blender/modifiers/intern/MOD_cast.c
+++ b/source/blender/modifiers/intern/MOD_cast.c
@@ -591,7 +591,6 @@ ModifierTypeInfo modifierType_Cast = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index a25d65347c5..40d027f3044 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -311,7 +311,6 @@ ModifierTypeInfo modifierType_Cloth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index e72e0279263..5dd57469914 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -317,7 +317,6 @@ ModifierTypeInfo modifierType_Collision = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index 001c7d8d098..fef235b456b 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -852,7 +852,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index d5d53edfd54..20dbb299767 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -236,7 +236,6 @@ ModifierTypeInfo modifierType_Curve = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index 8b299a82f95..dbdc76f0edc 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -495,7 +495,6 @@ ModifierTypeInfo modifierType_DataTransfer = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 03db09a5aec..faad1175f3a 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -300,7 +300,6 @@ ModifierTypeInfo modifierType_Decimate = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index abe78943508..a7ac9f618af 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -507,7 +507,6 @@ ModifierTypeInfo modifierType_Displace = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index 3e607e88cdd..8b1d541d45d 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_DynamicPaint = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c
index e02befd7efa..2874bebe13a 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -187,7 +187,6 @@ ModifierTypeInfo modifierType_EdgeSplit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index c12019a325e..e1197439c7c 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -1255,7 +1255,6 @@ ModifierTypeInfo modifierType_Explode = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index 8a8d6f2305f..36d2ab2a11a 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -239,7 +239,6 @@ ModifierTypeInfo modifierType_Fluid = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index 2fa05a319d5..ff581e92cdd 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -574,7 +574,6 @@ ModifierTypeInfo modifierType_Hook = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index bda0f9ba5a4..6efeec1970f 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -889,7 +889,6 @@ ModifierTypeInfo modifierType_LaplacianDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index fc527304e76..78e0bf3fa8f 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -635,7 +635,6 @@ ModifierTypeInfo modifierType_LaplacianSmooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ init_data,
/* requiredDataMask */ required_data_mask,
diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c
index e3c42e39dda..29d1ecf6050 100644
--- a/source/blender/modifiers/intern/MOD_lattice.c
+++ b/source/blender/modifiers/intern/MOD_lattice.c
@@ -193,7 +193,6 @@ ModifierTypeInfo modifierType_Lattice = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 191d39d9fce..a77f6cfe8ba 100644
--- a/source/blender/modifiers/intern/MOD_mask.cc
+++ b/source/blender/modifiers/intern/MOD_mask.cc
@@ -69,6 +69,19 @@ using blender::MutableSpan;
using blender::Span;
using blender::Vector;
+/* For delete geometry node. */
+void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map);
+void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map);
+void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts);
+
static void initData(ModifierData *md)
{
MaskModifierData *mmd = (MaskModifierData *)md;
@@ -237,9 +250,7 @@ static void computed_masked_polygons(const Mesh *mesh,
*r_num_masked_loops = num_masked_loops;
}
-static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map)
+void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
for (const int i_src : vertex_map.index_range()) {
@@ -256,10 +267,10 @@ static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
}
}
-static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map,
- Span<int> edge_map)
+void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
BLI_assert(src_mesh.totedge == edge_map.size());
@@ -279,12 +290,12 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
}
}
-static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map,
- Span<int> edge_map,
- Span<int> masked_poly_indices,
- Span<int> new_loop_starts)
+void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts)
{
for (const int i_dst : masked_poly_indices.index_range()) {
const int i_src = masked_poly_indices[i_dst];
@@ -463,7 +474,6 @@ ModifierTypeInfo modifierType_Mask = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
index cc007651733..778b5746471 100644
--- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
+++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
@@ -20,6 +20,7 @@
#include <vector>
+#include "BKE_geometry_set.hh"
#include "BKE_lib_query.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
@@ -204,7 +205,9 @@ static float compute_voxel_size(const ModifierEvalContext *ctx,
}
#endif
-static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *input_volume)
+static Volume *mesh_to_volume(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ Volume *input_volume)
{
#ifdef WITH_OPENVDB
using namespace blender;
@@ -278,6 +281,17 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
#endif
}
+static void modifyGeometrySet(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
+{
+ Volume *input_volume = geometry_set->get_volume_for_write();
+ Volume *result_volume = mesh_to_volume(md, ctx, input_volume);
+ if (result_volume != input_volume) {
+ geometry_set->replace_volume(result_volume);
+ }
+}
+
ModifierTypeInfo modifierType_MeshToVolume = {
/* name */ "Mesh to Volume",
/* structName */ "MeshToVolumeModifierData",
@@ -295,8 +309,7 @@ ModifierTypeInfo modifierType_MeshToVolume = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
- /* modifyGeometrySet */ nullptr,
- /* modifyVolume */ modifyVolume,
+ /* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index 6d2e0f242d7..361454120ca 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -389,7 +389,6 @@ ModifierTypeInfo modifierType_MeshCache = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index a94dd6da477..8e1e03570b5 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -644,7 +644,6 @@ ModifierTypeInfo modifierType_MeshDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
index 2c01857adb1..c2f9cd8c867 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.c
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c
@@ -271,7 +271,6 @@ ModifierTypeInfo modifierType_MeshSequenceCache = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c
index afe94d8dead..6116cf8146a 100644
--- a/source/blender/modifiers/intern/MOD_mirror.c
+++ b/source/blender/modifiers/intern/MOD_mirror.c
@@ -165,6 +165,13 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_mirror_merge"));
uiItemR(sub, ptr, "merge_threshold", 0, "", ICON_NONE);
+ bool is_bisect_set[3];
+ RNA_boolean_get_array(ptr, "use_bisect_axis", is_bisect_set);
+
+ sub = uiLayoutRow(col, true);
+ uiLayoutSetActive(sub, is_bisect_set[0] || is_bisect_set[1] || is_bisect_set[2]);
+ uiItemR(sub, ptr, "bisect_threshold", 0, IFACE_("Bisect Distance"), ICON_NONE);
+
modifier_panel_end(layout, ptr);
}
@@ -232,7 +239,6 @@ ModifierTypeInfo modifierType_Mirror = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index 1182c8db093..c3b34f3cd23 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -520,7 +520,6 @@ ModifierTypeInfo modifierType_Multires = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 6236dc87791..8fa80025790 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -29,12 +29,14 @@
#include "BLI_float3.hh"
#include "BLI_listbase.h"
+#include "BLI_multi_value_map.hh"
#include "BLI_set.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_collection_types.h"
#include "DNA_defaults.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
@@ -43,17 +45,23 @@
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_windowmanager_types.h"
#include "BKE_customdata.h"
+#include "BKE_geometry_set_instances.hh"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lib_query.h"
+#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_node_ui_storage.hh"
+#include "BKE_object.h"
#include "BKE_pointcloud.h"
#include "BKE_screen.h"
#include "BKE_simulation.h"
+#include "BKE_workspace.h"
#include "BLO_read_write.h"
@@ -68,13 +76,14 @@
#include "MOD_modifiertypes.h"
#include "MOD_nodes.h"
+#include "MOD_nodes_evaluator.hh"
#include "MOD_ui_common.h"
+#include "ED_spreadsheet.h"
+
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
-#include "NOD_geometry_exec.hh"
#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_callbacks.hh"
using blender::float3;
using blender::FunctionRef;
@@ -85,11 +94,8 @@ using blender::Span;
using blender::StringRef;
using blender::StringRefNull;
using blender::Vector;
-using blender::bke::PersistentCollectionHandle;
-using blender::bke::PersistentDataHandleMap;
-using blender::bke::PersistentObjectHandle;
using blender::fn::GMutablePointer;
-using blender::fn::GValueMap;
+using blender::fn::GPointer;
using blender::nodes::GeoNodeExecParams;
using namespace blender::fn::multi_function_types;
using namespace blender::nodes::derived_node_tree_types;
@@ -118,6 +124,18 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids)
ids.add(&collection->id);
}
}
+ else if (socket->type == SOCK_MATERIAL) {
+ Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value;
+ if (material != nullptr) {
+ ids.add(&material->id);
+ }
+ }
+ else if (socket->type == SOCK_TEXTURE) {
+ Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value;
+ if (texture != nullptr) {
+ ids.add(&texture->id);
+ }
+ }
}
}
@@ -129,7 +147,7 @@ static void find_used_ids_from_nodes(const bNodeTree &tree, Set<ID *> &ids)
addIdsUsedBySocket(&node->inputs, ids);
addIdsUsedBySocket(&node->outputs, ids);
- if (node->type == NODE_GROUP) {
+ if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
const bNodeTree *group = (bNodeTree *)node->id;
if (group != nullptr && handled_groups.add(group)) {
find_used_ids_from_nodes(*group, ids);
@@ -153,40 +171,34 @@ static void find_used_ids_from_settings(const NodesModifierSettings &settings, S
&ids);
}
-static void add_collection_object_relations_recursive(const ModifierUpdateDepsgraphContext *ctx,
- Collection &collection);
+/* We don't know exactly what attributes from the other object we will need. */
+static const CustomData_MeshMasks dependency_data_mask{CD_MASK_PROP_ALL | CD_MASK_MDEFORMVERT,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL};
+
+static void add_collection_relation(const ModifierUpdateDepsgraphContext *ctx,
+ Collection &collection)
+{
+ DEG_add_collection_geometry_relation(ctx->node, &collection, "Nodes Modifier");
+ DEG_add_collection_geometry_customdata_mask(ctx->node, &collection, &dependency_data_mask);
+}
static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Object &object)
{
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_TRANSFORM, "Nodes Modifier");
if (&(ID &)object != &ctx->object->id) {
- if (object.type == OB_EMPTY) {
- Collection *collection_instance = object.instance_collection;
- if (collection_instance != nullptr) {
- add_collection_object_relations_recursive(ctx, *collection_instance);
- }
+ if (object.type == OB_EMPTY && object.instance_collection != nullptr) {
+ add_collection_relation(ctx, *object.instance_collection);
}
- else {
+ else if (DEG_object_has_geometry_component(&object)) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
+ DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask);
}
}
}
-static void add_collection_object_relations_recursive(const ModifierUpdateDepsgraphContext *ctx,
- Collection &collection)
-{
- LISTBASE_FOREACH (CollectionObject *, collection_object, &collection.gobject) {
- BLI_assert(collection_object->ob != nullptr);
- Object &object = *collection_object->ob;
- add_object_relation(ctx, object);
- }
- LISTBASE_FOREACH (CollectionChild *, collection_child, &collection.children) {
- BLI_assert(collection_child->collection != nullptr);
- Collection &collection = *collection_child->collection;
- add_collection_object_relations_recursive(ctx, collection);
- }
-}
-
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
@@ -198,18 +210,28 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
find_used_ids_from_settings(nmd->settings, used_ids);
find_used_ids_from_nodes(*nmd->node_group, used_ids);
for (ID *id : used_ids) {
- if (GS(id->name) == ID_OB) {
- Object *object = reinterpret_cast<Object *>(id);
- add_object_relation(ctx, *object);
- }
- if (GS(id->name) == ID_GR) {
- Collection *collection = reinterpret_cast<Collection *>(id);
- add_collection_object_relations_recursive(ctx, *collection);
+ switch ((ID_Type)GS(id->name)) {
+ case ID_OB: {
+ Object *object = reinterpret_cast<Object *>(id);
+ add_object_relation(ctx, *object);
+ break;
+ }
+ case ID_GR: {
+ Collection *collection = reinterpret_cast<Collection *>(id);
+ add_collection_relation(ctx, *collection);
+ break;
+ }
+ case ID_TE: {
+ DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier");
+ }
+ default: {
+ /* Purposefully don't add relations for materials. While there are material sockets,
+ * the pointers are only passed around as handles rather than dereferenced. */
+ break;
+ }
}
}
}
-
- /* TODO: Add dependency for adding and removing objects in collections. */
}
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
@@ -252,368 +274,16 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
return false;
}
-class GeometryNodesEvaluator {
- private:
- blender::LinearAllocator<> allocator_;
- Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_;
- Vector<DInputSocket> group_outputs_;
- blender::nodes::MultiFunctionByNode &mf_by_node_;
- const blender::nodes::DataTypeConversions &conversions_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
-
- public:
- GeometryNodesEvaluator(const Map<DOutputSocket, GMutablePointer> &group_input_data,
- Vector<DInputSocket> group_outputs,
- blender::nodes::MultiFunctionByNode &mf_by_node,
- const PersistentDataHandleMap &handle_map,
- const Object *self_object,
- const ModifierData *modifier,
- Depsgraph *depsgraph)
- : group_outputs_(std::move(group_outputs)),
- mf_by_node_(mf_by_node),
- conversions_(blender::nodes::get_implicit_type_conversions()),
- handle_map_(handle_map),
- self_object_(self_object),
- modifier_(modifier),
- depsgraph_(depsgraph)
- {
- for (auto item : group_input_data.items()) {
- this->forward_to_inputs(item.key, item.value);
- }
- }
-
- Vector<GMutablePointer> execute()
- {
- Vector<GMutablePointer> results;
- for (const DInputSocket &group_output : group_outputs_) {
- Vector<GMutablePointer> result = this->get_input_values(group_output);
- results.append(result[0]);
- }
- for (GMutablePointer value : value_by_input_.values()) {
- value.destruct();
- }
- return results;
- }
-
- private:
- Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute)
- {
- Vector<DSocket> from_sockets;
- socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); });
-
- if (from_sockets.is_empty()) {
- /* The input is not connected, use the value from the socket itself. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- return {get_unlinked_input_value(socket_to_compute, type)};
- }
-
- /* Multi-input sockets contain a vector of inputs. */
- if (socket_to_compute->is_multi_input_socket()) {
- return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets);
- }
-
- const DSocket from_socket = from_sockets[0];
- GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket);
- return {value};
- }
-
- Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute,
- const Span<DSocket> from_sockets)
- {
- Vector<GMutablePointer> values;
- for (const int i : from_sockets.index_range()) {
- const DSocket from_socket = from_sockets[i];
- const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket);
- if (first_occurence == -1) {
- values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket));
- }
- else {
- /* If the same from-socket occurs more than once, we make a copy of the first value. This
- * can happen when a node linked to a multi-input-socket is muted. */
- GMutablePointer value = values[first_occurence];
- const CPPType *type = value.type();
- void *copy_buffer = allocator_.allocate(type->size(), type->alignment());
- type->copy_to_uninitialized(value.get(), copy_buffer);
- values.append({type, copy_buffer});
- }
- }
- return values;
- }
-
- GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute,
- const DSocket from_socket)
- {
- if (from_socket->is_output()) {
- const DOutputSocket from_output_socket{from_socket};
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute,
- from_output_socket);
- std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
- if (value.has_value()) {
- /* This input has been computed before, return it directly. */
- return {*value};
- }
-
- /* Compute the socket now. */
- this->compute_output_and_forward(from_output_socket);
- return {value_by_input_.pop(key)};
- }
-
- /* Get value from an unlinked input socket. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- const DInputSocket from_input_socket{from_socket};
- return {get_unlinked_input_value(from_input_socket, type)};
- }
-
- void compute_output_and_forward(const DOutputSocket socket_to_compute)
- {
- const DNode node{socket_to_compute.context(), &socket_to_compute->node()};
-
- if (!socket_to_compute->is_available()) {
- /* If the output is not available, use a default value. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(type.default_value(), buffer);
- this->forward_to_inputs(socket_to_compute, {type, buffer});
- return;
- }
-
- /* Prepare inputs required to execute the node. */
- GValueMap<StringRef> node_inputs_map{allocator_};
- for (const InputSocketRef *input_socket : node->inputs()) {
- if (input_socket->is_available()) {
- Vector<GMutablePointer> values = this->get_input_values({node.context(), input_socket});
- for (int i = 0; i < values.size(); ++i) {
- /* Values from Multi Input Sockets are stored in input map with the format
- * <identifier>[<index>]. */
- blender::StringRefNull key = allocator_.copy_string(
- input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : ""));
- node_inputs_map.add_new_direct(key, std::move(values[i]));
- }
- }
- }
-
- /* Execute the node. */
- GValueMap<StringRef> node_outputs_map{allocator_};
- GeoNodeExecParams params{
- node, node_inputs_map, node_outputs_map, handle_map_, self_object_, modifier_, depsgraph_};
- this->execute_node(node, params);
-
- /* Forward computed outputs to linked input sockets. */
- for (const OutputSocketRef *output_socket : node->outputs()) {
- if (output_socket->is_available()) {
- GMutablePointer value = node_outputs_map.extract(output_socket->identifier());
- this->forward_to_inputs({node.context(), output_socket}, value);
- }
- }
- }
-
- void execute_node(const DNode node, GeoNodeExecParams params)
- {
- const bNode &bnode = params.node();
-
- this->store_ui_hints(node, params);
-
- /* Use the geometry-node-execute callback if it exists. */
- if (bnode.typeinfo->geometry_node_execute != nullptr) {
- bnode.typeinfo->geometry_node_execute(params);
- return;
- }
-
- /* Use the multi-function implementation if it exists. */
- const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr);
- if (multi_function != nullptr) {
- this->execute_multi_function_node(node, params, *multi_function);
- return;
- }
-
- /* Just output default values if no implementation exists. */
- this->execute_unknown_node(node, params);
- }
-
- void store_ui_hints(const DNode node, GeoNodeExecParams params) const
- {
- for (const InputSocketRef *socket_ref : node->inputs()) {
- if (!socket_ref->is_available()) {
- continue;
- }
- if (socket_ref->bsocket()->type != SOCK_GEOMETRY) {
- continue;
- }
- if (socket_ref->is_multi_input_socket()) {
- /* Not needed currently. */
- continue;
- }
-
- bNodeTree *btree_cow = node->btree();
- bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
- const NodeTreeEvaluationContext context(*self_object_, *modifier_);
-
- const GeometrySet &geometry_set = params.get_input<GeometrySet>(socket_ref->identifier());
- const Vector<const GeometryComponent *> components = geometry_set.get_components_for_read();
-
- for (const GeometryComponent *component : components) {
- component->attribute_foreach(
- [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
- BKE_nodetree_attribute_hint_add(*btree_original,
- context,
- *node->bnode(),
- attribute_name,
- meta_data.domain,
- meta_data.data_type);
- return true;
- });
- }
- }
- }
-
- void execute_multi_function_node(const DNode node,
- GeoNodeExecParams params,
- const MultiFunction &fn)
- {
- MFContextBuilder fn_context;
- MFParamsBuilder fn_params{fn, 1};
- Vector<GMutablePointer> input_data;
- for (const InputSocketRef *socket_ref : node->inputs()) {
- if (socket_ref->is_available()) {
- GMutablePointer data = params.extract_input(socket_ref->identifier());
- fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1));
- input_data.append(data);
- }
- }
- Vector<GMutablePointer> output_data;
- for (const OutputSocketRef *socket_ref : node->outputs()) {
- if (socket_ref->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1));
- output_data.append(GMutablePointer(type, buffer));
- }
- }
- fn.call(IndexRange(1), fn_params, fn_context);
- for (GMutablePointer value : input_data) {
- value.destruct();
- }
- int output_index = 0;
- for (const int i : node->outputs().index_range()) {
- if (node->output(i).is_available()) {
- GMutablePointer value = output_data[output_index];
- params.set_output_by_move(node->output(i).identifier(), value);
- value.destruct();
- output_index++;
- }
- }
- }
-
- void execute_unknown_node(const DNode node, GeoNodeExecParams params)
- {
- for (const OutputSocketRef *socket : node->outputs()) {
- if (socket->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- params.set_output_by_copy(socket->identifier(), {type, type.default_value()});
- }
- }
- }
-
- void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward)
- {
- /* For all sockets that are linked with the from_socket push the value to their node. */
- Vector<DInputSocket> to_sockets_all;
- from_socket.foreach_target_socket(
- [&](DInputSocket to_socket) { to_sockets_all.append_non_duplicates(to_socket); });
-
- const CPPType &from_type = *value_to_forward.type();
- Vector<DInputSocket> to_sockets_same_type;
- for (const DInputSocket &to_socket : to_sockets_all) {
- const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- if (from_type == to_type) {
- to_sockets_same_type.append(to_socket);
- }
- else {
- void *buffer = allocator_.allocate(to_type.size(), to_type.alignment());
- if (conversions_.is_convertible(from_type, to_type)) {
- conversions_.convert(from_type, to_type, value_to_forward.get(), buffer);
- }
- else {
- to_type.copy_to_uninitialized(to_type.default_value(), buffer);
- }
- add_value_to_input_socket(key, GMutablePointer{to_type, buffer});
- }
- }
-
- if (to_sockets_same_type.size() == 0) {
- /* This value is not further used, so destruct it. */
- value_to_forward.destruct();
- }
- else if (to_sockets_same_type.size() == 1) {
- /* This value is only used on one input socket, no need to copy it. */
- const DInputSocket to_socket = to_sockets_same_type[0];
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
-
- add_value_to_input_socket(key, value_to_forward);
- }
- else {
- /* Multiple inputs use the value, make a copy for every input except for one. */
- const DInputSocket first_to_socket = to_sockets_same_type[0];
- Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
- const CPPType &type = *value_to_forward.type();
- const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket,
- from_socket);
- add_value_to_input_socket(first_key, value_to_forward);
- for (const DInputSocket &to_socket : other_to_sockets) {
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(value_to_forward.get(), buffer);
- add_value_to_input_socket(key, GMutablePointer{type, buffer});
- }
- }
- }
-
- void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key,
- GMutablePointer value)
- {
- value_by_input_.add_new(key, value);
+static bool logging_enabled(const ModifierEvalContext *ctx)
+{
+ if (!DEG_is_active(ctx->depsgraph)) {
+ return false;
}
-
- GMutablePointer get_unlinked_input_value(const DInputSocket &socket,
- const CPPType &required_type)
- {
- bNodeSocket *bsocket = socket->bsocket();
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
-
- if (bsocket->type == SOCK_OBJECT) {
- Object *object = socket->default_value<bNodeSocketValueObject>()->value;
- PersistentObjectHandle object_handle = handle_map_.lookup(object);
- new (buffer) PersistentObjectHandle(object_handle);
- }
- else if (bsocket->type == SOCK_COLLECTION) {
- Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value;
- PersistentCollectionHandle collection_handle = handle_map_.lookup(collection);
- new (buffer) PersistentCollectionHandle(collection_handle);
- }
- else {
- blender::nodes::socket_cpp_value_get(*bsocket, buffer);
- }
-
- if (type == required_type) {
- return {type, buffer};
- }
- if (conversions_.is_convertible(type, required_type)) {
- void *converted_buffer = allocator_.allocate(required_type.size(),
- required_type.alignment());
- conversions_.convert(type, required_type, buffer, converted_buffer);
- type.destruct(buffer);
- return {required_type, converted_buffer};
- }
- void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment());
- required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
- return {required_type, default_buffer};
+ if ((ctx->flag & MOD_APPLY_ORCO) != 0) {
+ return false;
}
-};
+ return true;
+}
/**
* This code is responsible for creating the new property and also creating the group of
@@ -634,9 +304,7 @@ struct SocketPropertyType {
IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name);
PropertyType (*rna_subtype_get)(const bNodeSocket &socket);
bool (*is_correct_type)(const IDProperty &property);
- void (*init_cpp_value)(const IDProperty &property,
- const PersistentDataHandleMap &handles,
- void *r_value);
+ void (*init_cpp_value)(const IDProperty &property, void *r_value);
};
static IDProperty *socket_add_property(IDProperty *settings_prop_group,
@@ -659,6 +327,14 @@ static IDProperty *socket_add_property(IDProperty *settings_prop_group,
IDP_AddToGroup(ui_container, prop_ui_group);
}
+ /* Set property description (tooltip). */
+ IDPropertyTemplate property_description_template;
+ property_description_template.string.str = socket.description;
+ property_description_template.string.len = BLI_strnlen(socket.description, MAX_NAME) + 1;
+ property_description_template.string.subtype = IDP_STRING_SUB_UTF8;
+ IDProperty *description = IDP_New(IDP_STRING, &property_description_template, "description");
+ IDP_AddToGroup(prop_ui_group, description);
+
/* Create the properties for the socket's UI settings. */
if (property_type.create_min_ui_prop != nullptr) {
IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "min"));
@@ -720,10 +396,15 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
[](const bNodeSocket &socket) {
return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype;
},
- [](const IDProperty &property) { return property.type == IDP_FLOAT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(float *)r_value = IDP_Float(&property); },
+ [](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
+ [](const IDProperty &property, void *r_value) {
+ if (property.type == IDP_FLOAT) {
+ *(float *)r_value = IDP_Float(&property);
+ }
+ else if (property.type == IDP_DOUBLE) {
+ *(float *)r_value = (float)IDP_Double(&property);
+ }
+ },
};
return &float_type;
}
@@ -757,9 +438,7 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
},
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(int *)r_value = IDP_Int(&property); },
+ [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
};
return &int_type;
}
@@ -802,9 +481,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT &&
property.len == 3;
},
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); },
+ [](const IDProperty &property, void *r_value) {
+ copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
+ },
};
return &vector_type;
}
@@ -834,9 +513,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
nullptr,
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(bool *)r_value = IDP_Int(&property) != 0; },
+ [](const IDProperty &property, void *r_value) {
+ *(bool *)r_value = IDP_Int(&property) != 0;
+ },
};
return &boolean_type;
}
@@ -856,9 +535,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
nullptr,
[](const IDProperty &property) { return property.type == IDP_STRING; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { new (r_value) std::string(IDP_String(&property)); },
+ [](const IDProperty &property, void *r_value) {
+ new (r_value) std::string(IDP_String(&property));
+ },
};
return &string_type;
}
@@ -875,10 +554,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
nullptr,
[](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) {
+ [](const IDProperty &property, void *r_value) {
ID *id = IDP_Id(&property);
Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
- new (r_value) PersistentObjectHandle(handles.lookup(object));
+ *(Object **)r_value = object;
},
};
return &object_type;
@@ -896,10 +575,52 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
nullptr,
[](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) {
+ [](const IDProperty &property, void *r_value) {
ID *id = IDP_Id(&property);
Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
- new (r_value) PersistentCollectionHandle(handles.lookup(collection));
+ *(Collection **)r_value = collection;
+ },
+ };
+ return &collection_type;
+ }
+ case SOCK_TEXTURE: {
+ static const SocketPropertyType collection_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, name);
+ },
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ [](const IDProperty &property) { return property.type == IDP_ID; },
+ [](const IDProperty &property, void *r_value) {
+ ID *id = IDP_Id(&property);
+ Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
+ *(Tex **)r_value = texture;
+ },
+ };
+ return &collection_type;
+ }
+ case SOCK_MATERIAL: {
+ static const SocketPropertyType collection_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, name);
+ },
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ [](const IDProperty &property) { return property.type == IDP_ID; },
+ [](const IDProperty &property, void *r_value) {
+ ID *id = IDP_Id(&property);
+ Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
+ *(Material **)r_value = material;
},
};
return &collection_type;
@@ -987,7 +708,6 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
}
static void initialize_group_input(NodesModifierData &nmd,
- const PersistentDataHandleMap &handle_map,
const bNodeSocket &socket,
const CPPType &cpp_type,
void *r_value)
@@ -1011,22 +731,7 @@ static void initialize_group_input(NodesModifierData &nmd,
blender::nodes::socket_cpp_value_get(socket, r_value);
return;
}
- property_type->init_cpp_value(*property, handle_map, r_value);
-}
-
-static void fill_data_handle_map(const NodesModifierSettings &settings,
- const DerivedNodeTree &tree,
- PersistentDataHandleMap &handle_map)
-{
- Set<ID *> used_ids;
- find_used_ids_from_settings(settings, used_ids);
- find_used_ids_from_nodes(*tree.root_context().tree().btree(), used_ids);
-
- int current_handle = 0;
- for (ID *id : used_ids) {
- handle_map.add(current_handle, *id);
- current_handle++;
- }
+ property_type->init_cpp_value(*property, r_value);
}
static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> trees,
@@ -1042,29 +747,190 @@ static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> tree
}
}
+static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
+{
+ wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
+ if (wm == nullptr) {
+ return {};
+ }
+ Vector<SpaceSpreadsheet *> spreadsheets;
+ LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
+ bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = (SpaceLink *)area->spacedata.first;
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ spreadsheets.append((SpaceSpreadsheet *)sl);
+ }
+ }
+ }
+ return spreadsheets;
+}
+
+using PreviewSocketMap = blender::MultiValueMap<DSocket, uint64_t>;
+
+static DSocket try_find_preview_socket_in_node(const DNode node)
+{
+ for (const SocketRef *socket : node->outputs()) {
+ if (socket->bsocket()->type == SOCK_GEOMETRY) {
+ return {node.context(), socket};
+ }
+ }
+ for (const SocketRef *socket : node->inputs()) {
+ if (socket->bsocket()->type == SOCK_GEOMETRY &&
+ (socket->bsocket()->flag & SOCK_MULTI_INPUT) == 0) {
+ return {node.context(), socket};
+ }
+ }
+ return {};
+}
+
+static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet,
+ NodesModifierData *nmd,
+ const ModifierEvalContext *ctx,
+ const DerivedNodeTree &tree)
+{
+ Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ if (context_path.size() < 3) {
+ return {};
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return {};
+ }
+ if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
+ return {};
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
+ if (object_context->object != DEG_get_original_object(ctx->object)) {
+ return {};
+ }
+ SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context_path[1];
+ if (StringRef(modifier_context->modifier_name) != nmd->modifier.name) {
+ return {};
+ }
+ for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) {
+ if (context->type != SPREADSHEET_CONTEXT_NODE) {
+ return {};
+ }
+ }
+
+ Span<SpreadsheetContextNode *> nested_group_contexts =
+ context_path.as_span().drop_front(2).drop_back(1).cast<SpreadsheetContextNode *>();
+ SpreadsheetContextNode *last_context = (SpreadsheetContextNode *)context_path.last();
+
+ const DTreeContext *context = &tree.root_context();
+ for (SpreadsheetContextNode *node_context : nested_group_contexts) {
+ const NodeTreeRef &tree_ref = context->tree();
+ const NodeRef *found_node = nullptr;
+ for (const NodeRef *node_ref : tree_ref.nodes()) {
+ if (node_ref->name() == node_context->node_name) {
+ found_node = node_ref;
+ break;
+ }
+ }
+ if (found_node == nullptr) {
+ return {};
+ }
+ context = context->child_context(*found_node);
+ if (context == nullptr) {
+ return {};
+ }
+ }
+
+ const NodeTreeRef &tree_ref = context->tree();
+ for (const NodeRef *node_ref : tree_ref.nodes()) {
+ if (node_ref->name() == last_context->node_name) {
+ return try_find_preview_socket_in_node({context, node_ref});
+ }
+ }
+ return {};
+}
+
+static void find_sockets_to_preview(NodesModifierData *nmd,
+ const ModifierEvalContext *ctx,
+ const DerivedNodeTree &tree,
+ PreviewSocketMap &r_sockets_to_preview)
+{
+ Main *bmain = DEG_get_bmain(ctx->depsgraph);
+
+ /* Based on every visible spreadsheet context path, get a list of sockets that need to have their
+ * intermediate geometries cached for display. */
+ Vector<SpaceSpreadsheet *> spreadsheets = find_spreadsheet_editors(bmain);
+ for (SpaceSpreadsheet *sspreadsheet : spreadsheets) {
+ const DSocket socket = try_get_socket_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree);
+ if (socket) {
+ const uint64_t key = ED_spreadsheet_context_path_hash(sspreadsheet);
+ r_sockets_to_preview.add_non_duplicates(socket, key);
+ }
+ }
+}
+
+static void log_preview_socket_value(const Span<GPointer> values,
+ Object *object,
+ Span<uint64_t> keys)
+{
+ GeometrySet geometry_set = *(const GeometrySet *)values[0].get();
+ geometry_set.ensure_owns_direct_data();
+ for (uint64_t key : keys) {
+ BKE_object_preview_geometry_set_add(object, key, new GeometrySet(geometry_set));
+ }
+}
+
+static void log_ui_hints(const DSocket socket,
+ const Span<GPointer> values,
+ Object *self_object,
+ NodesModifierData *nmd)
+{
+ const DNode node = socket.node();
+ if (node->is_reroute_node() || socket->typeinfo()->type != SOCK_GEOMETRY) {
+ return;
+ }
+ bNodeTree *btree_cow = node->btree();
+ bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
+ const NodeTreeEvaluationContext context{*self_object, nmd->modifier};
+ for (const GPointer &data : values) {
+ if (data.type() == &CPPType::get<GeometrySet>()) {
+ const GeometrySet &geometry_set = *(const GeometrySet *)data.get();
+ blender::bke::geometry_set_instances_attribute_foreach(
+ geometry_set,
+ [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
+ BKE_nodetree_attribute_hint_add(*btree_original,
+ context,
+ *node->bnode(),
+ attribute_name,
+ meta_data.domain,
+ meta_data.data_type);
+ return true;
+ },
+ 8);
+ }
+ }
+}
+
/**
* Evaluate a node group to compute the output geometry.
* Currently, this uses a fairly basic and inefficient algorithm that might compute things more
* often than necessary. It's going to be replaced soon.
*/
static GeometrySet compute_geometry(const DerivedNodeTree &tree,
- Span<const OutputSocketRef *> group_input_sockets,
+ Span<const NodeRef *> group_input_nodes,
const InputSocketRef &socket_to_compute,
GeometrySet input_geometry_set,
NodesModifierData *nmd,
const ModifierEvalContext *ctx)
{
- blender::ResourceCollector resources;
- blender::LinearAllocator<> &allocator = resources.linear_allocator();
- blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, resources);
-
- PersistentDataHandleMap handle_map;
- fill_data_handle_map(nmd->settings, tree, handle_map);
+ blender::ResourceScope scope;
+ blender::LinearAllocator<> &allocator = scope.linear_allocator();
+ blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope);
Map<DOutputSocket, GMutablePointer> group_inputs;
const DTreeContext *root_context = &tree.root_context();
- if (group_input_sockets.size() > 0) {
+ for (const NodeRef *group_input_node : group_input_nodes) {
+ Span<const OutputSocketRef *> group_input_sockets = group_input_node->outputs().drop_back(1);
+ if (group_input_sockets.is_empty()) {
+ continue;
+ }
+
Span<const OutputSocketRef *> remaining_input_sockets = group_input_sockets;
/* If the group expects a geometry as first input, use the geometry that has been passed to
@@ -1072,7 +938,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
const OutputSocketRef *first_input_socket = group_input_sockets[0];
if (first_input_socket->bsocket()->type == SOCK_GEOMETRY) {
GeometrySet *geometry_set_in =
- allocator.construct<GeometrySet>(std::move(input_geometry_set)).release();
+ allocator.construct<GeometrySet>(input_geometry_set).release();
group_inputs.add_new({root_context, first_input_socket}, geometry_set_in);
remaining_input_sockets = remaining_input_sockets.drop_front(1);
}
@@ -1081,28 +947,44 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
- initialize_group_input(*nmd, handle_map, *socket->bsocket(), cpp_type, value_in);
+ initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
+ /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */
+ input_geometry_set.clear();
+
Vector<DInputSocket> group_outputs;
group_outputs.append({root_context, &socket_to_compute});
- GeometryNodesEvaluator evaluator{group_inputs,
- group_outputs,
- mf_by_node,
- handle_map,
- ctx->object,
- (ModifierData *)nmd,
- ctx->depsgraph};
+ PreviewSocketMap preview_sockets;
+ find_sockets_to_preview(nmd, ctx, tree, preview_sockets);
- Vector<GMutablePointer> results = evaluator.execute();
- BLI_assert(results.size() == 1);
- GMutablePointer result = results[0];
-
- GeometrySet output_geometry = std::move(*(GeometrySet *)result.get());
- return output_geometry;
+ auto log_socket_value = [&](const DSocket socket, const Span<GPointer> values) {
+ if (!logging_enabled(ctx)) {
+ return;
+ }
+ Span<uint64_t> keys = preview_sockets.lookup(socket);
+ if (!keys.is_empty()) {
+ log_preview_socket_value(values, ctx->object, keys);
+ }
+ log_ui_hints(socket, values, ctx->object, nmd);
+ };
+
+ blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params;
+ eval_params.input_values = group_inputs;
+ eval_params.output_sockets = group_outputs;
+ eval_params.mf_by_node = &mf_by_node;
+ eval_params.modifier_ = nmd;
+ eval_params.depsgraph = ctx->depsgraph;
+ eval_params.self_object = ctx->object;
+ eval_params.log_socket_value_fn = log_socket_value;
+ blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params);
+
+ BLI_assert(eval_params.r_output_values.size() == 1);
+ GMutablePointer result = eval_params.r_output_values[0];
+ return result.relocate_out<GeometrySet>();
}
/**
@@ -1174,18 +1056,10 @@ static void modifyGeometry(ModifierData *md,
Span<const NodeRef *> input_nodes = root_tree_ref.nodes_by_type("NodeGroupInput");
Span<const NodeRef *> output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput");
- if (input_nodes.size() > 1) {
- return;
- }
if (output_nodes.size() != 1) {
return;
}
- Span<const OutputSocketRef *> group_inputs;
- if (input_nodes.size() == 1) {
- group_inputs = input_nodes[0]->outputs().drop_back(1);
- }
-
Span<const InputSocketRef *> group_outputs = output_nodes[0]->inputs().drop_back(1);
if (group_outputs.size() == 0) {
@@ -1197,10 +1071,12 @@ static void modifyGeometry(ModifierData *md,
return;
}
- reset_tree_ui_storage(tree.used_node_tree_refs(), *ctx->object, *md);
+ if (logging_enabled(ctx)) {
+ reset_tree_ui_storage(tree.used_node_tree_refs(), *ctx->object, *md);
+ }
geometry_set = compute_geometry(
- tree, group_inputs, *group_outputs[0], std::move(geometry_set), nmd, ctx);
+ tree, input_nodes, *group_outputs[0], std::move(geometry_set), nmd, ctx);
}
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
@@ -1209,6 +1085,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
geometry_set.get_component_for_write<MeshComponent>().copy_vertex_group_names_from_object(
*ctx->object);
modifyGeometry(md, ctx, geometry_set);
+
+ /* This function is only called when applying modifiers. In this case it makes sense to realize
+ * instances, otherwise in some cases there might be no results when applying the modifier. */
+ geometry_set = blender::bke::geometry_set_realize_mesh_for_modifier(geometry_set);
+
Mesh *new_mesh = geometry_set.get_component_for_write<MeshComponent>().release();
if (new_mesh == nullptr) {
return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
@@ -1242,36 +1123,45 @@ 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)) {
+ if (property == nullptr || !property_type->is_correct_type(*property)) {
+ return;
+ }
- char socket_id_esc[sizeof(socket.identifier) * 2];
- BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
+ 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);
+ char rna_path[sizeof(socket_id_esc) + 4];
+ BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
- /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
- * information about what type of ID to select for editing the values. This is because
- * pointer IDProperties contain no information about their type. */
- switch (socket.type) {
- case SOCK_OBJECT: {
- uiItemPointerR(
- layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
- break;
- }
- case SOCK_COLLECTION: {
- uiItemPointerR(layout,
- md_ptr,
- rna_path,
- bmain_ptr,
- "collections",
- socket.name,
- ICON_OUTLINER_COLLECTION);
- break;
- }
- default:
- uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
+ /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
+ * information about what type of ID to select for editing the values. This is because
+ * pointer IDProperties contain no information about their type. */
+ switch (socket.type) {
+ case SOCK_OBJECT: {
+ uiItemPointerR(
+ layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
+ break;
+ }
+ case SOCK_COLLECTION: {
+ uiItemPointerR(layout,
+ md_ptr,
+ rna_path,
+ bmain_ptr,
+ "collections",
+ socket.name,
+ ICON_OUTLINER_COLLECTION);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "materials", socket.name, ICON_MATERIAL);
+ break;
+ }
+ case SOCK_TEXTURE: {
+ uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE);
+ break;
}
+ default:
+ uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
}
}
@@ -1359,6 +1249,7 @@ static void requiredDataMask(Object *UNUSED(ob),
/* 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;
+ r_cddata_masks->vmask |= CD_MASK_PROP_ALL;
}
ModifierTypeInfo modifierType_Nodes = {
@@ -1382,7 +1273,6 @@ ModifierTypeInfo modifierType_Nodes = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ modifyGeometrySet,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
new file mode 100644
index 00000000000..170ad0f2c48
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -0,0 +1,1553 @@
+/*
+ * 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 "MOD_nodes_evaluator.hh"
+
+#include "NOD_geometry_exec.hh"
+#include "NOD_type_conversions.hh"
+
+#include "DEG_depsgraph_query.h"
+
+#include "FN_generic_value_map.hh"
+#include "FN_multi_function.hh"
+
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_stack.hh"
+#include "BLI_task.h"
+#include "BLI_task.hh"
+#include "BLI_vector_set.hh"
+
+namespace blender::modifiers::geometry_nodes {
+
+using fn::CPPType;
+using fn::GValueMap;
+using nodes::GeoNodeExecParams;
+using namespace fn::multi_function_types;
+
+enum class ValueUsage : uint8_t {
+ /* The value is definitely used. */
+ Required,
+ /* The value may be used. */
+ Maybe,
+ /* The value will definitely not be used. */
+ Unused,
+};
+
+struct SingleInputValue {
+ /**
+ * Points either to null or to a value of the type of input.
+ */
+ void *value = nullptr;
+};
+
+struct MultiInputValueItem {
+ /**
+ * The socket where this value is coming from. This is required to sort the inputs correctly
+ * based on the link order later on.
+ */
+ DSocket origin;
+ /**
+ * Should only be null directly after construction. After that it should always point to a value
+ * of the correct type.
+ */
+ void *value = nullptr;
+};
+
+struct MultiInputValue {
+ /**
+ * Collection of all the inputs that have been provided already. Note, the same origin can occur
+ * multiple times. However, it is guaranteed that if two items have the same origin, they will
+ * also have the same value (the pointer is different, but they point to values that would
+ * compare equal).
+ */
+ Vector<MultiInputValueItem> items;
+ /**
+ * Number of items that need to be added until all inputs have been provided.
+ */
+ int expected_size = 0;
+};
+
+struct InputState {
+
+ /**
+ * Type of the socket. If this is null, the socket should just be ignored.
+ */
+ const CPPType *type = nullptr;
+
+ /**
+ * Value of this input socket. By default, the value is empty. When other nodes are done
+ * computing their outputs, the computed values will be forwarded to linked input sockets.
+ * The value will then live here until it is consumed by the node or it was found that the value
+ * is not needed anymore.
+ * Whether the `single` or `multi` value is used depends on the socket.
+ */
+ union {
+ SingleInputValue *single;
+ MultiInputValue *multi;
+ } value;
+
+ /**
+ * How the node intends to use this input. By default all inputs may be used. Based on which
+ * outputs are used, a node can tell the evaluator that an input will definitely be used or is
+ * never used. This allows the evaluator to free values early, avoid copies and other unnecessary
+ * computations.
+ */
+ ValueUsage usage = ValueUsage::Maybe;
+
+ /**
+ * True when this input is/was used for an execution. While a node is running, only the inputs
+ * that have this set to true are allowed to be used. This makes sure that inputs created while
+ * the node is running correctly trigger the node to run again. Furthermore, it gives the node a
+ * consistent view of which inputs are available that does not change unexpectedly.
+ *
+ * While the node is running, this can be checked without a lock, because no one is writing to
+ * it. If this is true, the value can be read without a lock as well, because the value is not
+ * changed by others anymore.
+ */
+ bool was_ready_for_execution = false;
+};
+
+struct OutputState {
+ /**
+ * If this output has been computed and forwarded already. If this is true, the value is not
+ * computed/forwarded again.
+ */
+ bool has_been_computed = false;
+
+ /**
+ * Keeps track of how the output value is used. If a connected input becomes required, this
+ * output has to become required as well. The output becomes ignored when it has zero potential
+ * users that are counted below.
+ */
+ ValueUsage output_usage = ValueUsage::Maybe;
+
+ /**
+ * This is a copy of `output_usage` that is done right before node execution starts. This is
+ * done so that the node gets a consistent view of what outputs are used, even when this changes
+ * while the node is running (the node might be reevaluated in that case).
+ *
+ * While the node is running, this can be checked without a lock, because no one is writing to
+ * it.
+ */
+ ValueUsage output_usage_for_execution = ValueUsage::Maybe;
+
+ /**
+ * Counts how many times the value from this output might be used. If this number reaches zero,
+ * the output is not needed anymore.
+ */
+ int potential_users = 0;
+};
+
+enum class NodeScheduleState {
+ /**
+ * Default state of every node.
+ */
+ NotScheduled,
+ /**
+ * The node has been added to the task group and will be executed by it in the future.
+ */
+ Scheduled,
+ /**
+ * The node is currently running.
+ */
+ Running,
+ /**
+ * The node is running and has been rescheduled while running. In this case the node will run
+ * again. However, we don't add it to the task group immediately, because then the node might run
+ * twice at the same time, which is not allowed. Instead, once the node is done running, it will
+ * reschedule itself.
+ */
+ RunningAndRescheduled,
+};
+
+struct NodeState {
+ /**
+ * Needs to be locked when any data in this state is accessed that is not explicitly marked as
+ * otherwise.
+ */
+ std::mutex mutex;
+
+ /**
+ * States of the individual input and output sockets. One can index into these arrays without
+ * locking. However, to access the data inside a lock is generally necessary.
+ *
+ * These spans have to be indexed with the socket index. Unavailable sockets have a state as
+ * well. Maybe we can handle unavailable sockets differently in Blender in general, so I did not
+ * want to add complexity around it here.
+ */
+ MutableSpan<InputState> inputs;
+ MutableSpan<OutputState> outputs;
+
+ /**
+ * Nodes that don't support laziness have some special handling the first time they are executed.
+ */
+ bool non_lazy_node_is_initialized = false;
+
+ /**
+ * Used to check that nodes that don't support laziness do not run more than once.
+ */
+ bool has_been_executed = false;
+
+ /**
+ * Becomes true when the node will never be executed again and its inputs are destructed.
+ * Generally, a node has finished once all of its outputs with (potential) users have been
+ * computed.
+ */
+ bool node_has_finished = false;
+
+ /**
+ * Counts the number of values that still have to be forwarded to this node until it should run
+ * again. It counts values from a multi input socket separately.
+ * This is used as an optimization so that nodes are not scheduled unnecessarily in many cases.
+ */
+ int missing_required_inputs = 0;
+
+ /**
+ * A node is always in one specific schedule state. This helps to ensure that the same node does
+ * not run twice at the same time accidentally.
+ */
+ NodeScheduleState schedule_state = NodeScheduleState::NotScheduled;
+};
+
+/**
+ * Container for a node and its state. Packing them into a single struct allows the use of
+ * `VectorSet` instead of a `Map` for `node_states_` which simplifies parallel loops over all
+ * states.
+ *
+ * Equality operators and a hash function for `DNode` are provided so that one can lookup this type
+ * in `node_states_` just with a `DNode`.
+ */
+struct NodeWithState {
+ DNode node;
+ /* Store a pointer instead of `NodeState` directly to keep it small and movable. */
+ NodeState *state = nullptr;
+
+ friend bool operator==(const NodeWithState &a, const NodeWithState &b)
+ {
+ return a.node == b.node;
+ }
+
+ friend bool operator==(const NodeWithState &a, const DNode &b)
+ {
+ return a.node == b;
+ }
+
+ friend bool operator==(const DNode &a, const NodeWithState &b)
+ {
+ return a == b.node;
+ }
+
+ uint64_t hash() const
+ {
+ return node.hash();
+ }
+
+ static uint64_t hash_as(const DNode &node)
+ {
+ return node.hash();
+ }
+};
+
+class GeometryNodesEvaluator;
+
+/**
+ * Utility class that locks the state of a node. Having this is a separate class is useful because
+ * it allows methods to communicate that they expect the node to be locked.
+ */
+class LockedNode : NonCopyable, NonMovable {
+ private:
+ GeometryNodesEvaluator &evaluator_;
+
+ public:
+ /**
+ * This is the node that is currently locked.
+ */
+ const DNode node;
+ NodeState &node_state;
+
+ /**
+ * Used to delay notifying (and therefore locking) other nodes until the current node is not
+ * locked anymore. This might not be strictly necessary to avoid deadlocks in the current code,
+ * but it is a good measure to avoid accidentally adding a deadlock later on. By not locking
+ * more than one node per thread at a time, deadlocks are avoided.
+ *
+ * The notifications will be send right after the node is not locked anymore.
+ */
+ Vector<DOutputSocket> delayed_required_outputs;
+ Vector<DOutputSocket> delayed_unused_outputs;
+ Vector<DNode> delayed_scheduled_nodes;
+
+ LockedNode(GeometryNodesEvaluator &evaluator, const DNode node, NodeState &node_state)
+ : evaluator_(evaluator), node(node), node_state(node_state)
+ {
+ node_state.mutex.lock();
+ }
+
+ ~LockedNode();
+};
+
+static const CPPType *get_socket_cpp_type(const DSocket socket)
+{
+ return nodes::socket_cpp_type_get(*socket->typeinfo());
+}
+
+static const CPPType *get_socket_cpp_type(const SocketRef &socket)
+{
+ return nodes::socket_cpp_type_get(*socket.typeinfo());
+}
+
+static bool node_supports_laziness(const DNode node)
+{
+ return node->typeinfo()->geometry_node_execute_supports_laziness;
+}
+
+/** Implements the callbacks that might be called when a node is executed. */
+class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider {
+ private:
+ GeometryNodesEvaluator &evaluator_;
+ NodeState &node_state_;
+
+ public:
+ NodeParamsProvider(GeometryNodesEvaluator &evaluator, DNode dnode, NodeState &node_state);
+
+ bool can_get_input(StringRef identifier) const override;
+ bool can_set_output(StringRef identifier) const override;
+ GMutablePointer extract_input(StringRef identifier) override;
+ Vector<GMutablePointer> extract_multi_input(StringRef identifier) override;
+ GPointer get_input(StringRef identifier) const override;
+ GMutablePointer alloc_output_value(const CPPType &type) override;
+ void set_output(StringRef identifier, GMutablePointer value) override;
+ void set_input_unused(StringRef identifier) override;
+ bool output_is_required(StringRef identifier) const override;
+
+ bool lazy_require_input(StringRef identifier) override;
+ bool lazy_output_is_required(StringRef identifier) const override;
+};
+
+class GeometryNodesEvaluator {
+ private:
+ /**
+ * This allocator lives on after the evaluator has been destructed. Therefore outputs of the
+ * entire evaluator should be allocated here.
+ */
+ LinearAllocator<> &outer_allocator_;
+ /**
+ * A local linear allocator for each thread. Only use this for values that do not need to live
+ * longer than the lifetime of the evaluator itself. Considerations for the future:
+ * - We could use an allocator that can free here, some temporary values don't live long.
+ * - If we ever run into false sharing bottlenecks, we could use local allocators that allocate
+ * on cache line boundaries. Note, just because a value is allocated in one specific thread,
+ * does not mean that it will only be used by that thread.
+ */
+ EnumerableThreadSpecific<LinearAllocator<>> local_allocators_;
+
+ /**
+ * Every node that is reachable from the output gets its own state. Once all states have been
+ * constructed, this map can be used for lookups from multiple threads.
+ */
+ VectorSet<NodeWithState> node_states_;
+
+ /**
+ * Contains all the tasks for the nodes that are currently scheduled.
+ */
+ TaskPool *task_pool_ = nullptr;
+
+ GeometryNodesEvaluationParams &params_;
+ const blender::nodes::DataTypeConversions &conversions_;
+
+ friend NodeParamsProvider;
+
+ public:
+ GeometryNodesEvaluator(GeometryNodesEvaluationParams &params)
+ : outer_allocator_(params.allocator),
+ params_(params),
+ conversions_(blender::nodes::get_implicit_type_conversions())
+ {
+ }
+
+ void execute()
+ {
+ task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH, TASK_ISOLATION_OFF);
+
+ this->create_states_for_reachable_nodes();
+ this->forward_group_inputs();
+ this->schedule_initial_nodes();
+
+ /* This runs until all initially requested inputs have been computed. */
+ BLI_task_pool_work_and_wait(task_pool_);
+ BLI_task_pool_free(task_pool_);
+
+ this->extract_group_outputs();
+ this->destruct_node_states();
+ }
+
+ void create_states_for_reachable_nodes()
+ {
+ /* This does a depth first search for all the nodes that are reachable from the group
+ * outputs. This finds all nodes that are relevant. */
+ Stack<DNode> nodes_to_check;
+ /* Start at the output sockets. */
+ for (const DInputSocket &socket : params_.output_sockets) {
+ nodes_to_check.push(socket.node());
+ }
+ /* Use the local allocator because the states do not need to outlive the evaluator. */
+ LinearAllocator<> &allocator = local_allocators_.local();
+ while (!nodes_to_check.is_empty()) {
+ const DNode node = nodes_to_check.pop();
+ if (node_states_.contains_as(node)) {
+ /* This node has been handled already. */
+ continue;
+ }
+ /* Create a new state for the node. */
+ NodeState &node_state = *allocator.construct<NodeState>().release();
+ node_states_.add_new({node, &node_state});
+
+ /* Push all linked origins on the stack. */
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+ input.foreach_origin_socket(
+ [&](const DSocket origin) { nodes_to_check.push(origin.node()); });
+ }
+ }
+
+ /* Initialize the more complex parts of the node states in parallel. At this point no new
+ * node states are added anymore, so it is safe to lookup states from `node_states_` from
+ * multiple threads. */
+ parallel_for(IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) {
+ LinearAllocator<> &allocator = this->local_allocators_.local();
+ for (const NodeWithState &item : node_states_.as_span().slice(range)) {
+ this->initialize_node_state(item.node, *item.state, allocator);
+ }
+ });
+ }
+
+ void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator)
+ {
+ /* Construct arrays of the correct size. */
+ node_state.inputs = allocator.construct_array<InputState>(node->inputs().size());
+ node_state.outputs = allocator.construct_array<OutputState>(node->outputs().size());
+
+ /* Initialize input states. */
+ for (const int i : node->inputs().index_range()) {
+ InputState &input_state = node_state.inputs[i];
+ const DInputSocket socket = node.input(i);
+ if (!socket->is_available()) {
+ /* Unavailable sockets should never be used. */
+ input_state.type = nullptr;
+ input_state.usage = ValueUsage::Unused;
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(socket);
+ input_state.type = type;
+ if (type == nullptr) {
+ /* This is not a known data socket, it shouldn't be used. */
+ input_state.usage = ValueUsage::Unused;
+ continue;
+ }
+ /* Construct the correct struct that can hold the input(s). */
+ if (socket->is_multi_input_socket()) {
+ input_state.value.multi = allocator.construct<MultiInputValue>().release();
+ /* Count how many values should be added until the socket is complete. */
+ socket.foreach_origin_socket(
+ [&](DSocket UNUSED(origin)) { input_state.value.multi->expected_size++; });
+ /* If no links are connected, we do read the value from socket itself. */
+ if (input_state.value.multi->expected_size == 0) {
+ input_state.value.multi->expected_size = 1;
+ }
+ }
+ else {
+ input_state.value.single = allocator.construct<SingleInputValue>().release();
+ }
+ }
+ /* Initialize output states. */
+ for (const int i : node->outputs().index_range()) {
+ OutputState &output_state = node_state.outputs[i];
+ const DOutputSocket socket = node.output(i);
+ if (!socket->is_available()) {
+ /* Unavailable outputs should never be used. */
+ output_state.output_usage = ValueUsage::Unused;
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(socket);
+ if (type == nullptr) {
+ /* Non data sockets should never be used. */
+ output_state.output_usage = ValueUsage::Unused;
+ continue;
+ }
+ /* Count the number of potential users for this socket. */
+ socket.foreach_target_socket(
+ [&, this](const DInputSocket target_socket) {
+ const DNode target_node = target_socket.node();
+ if (!this->node_states_.contains_as(target_node)) {
+ /* The target node is not computed because it is not computed to the output. */
+ return;
+ }
+ output_state.potential_users += 1;
+ },
+ {});
+ if (output_state.potential_users == 0) {
+ /* If it does not have any potential users, it is unused. */
+ output_state.output_usage = ValueUsage::Unused;
+ }
+ }
+ }
+
+ void destruct_node_states()
+ {
+ parallel_for(IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) {
+ for (const NodeWithState &item : node_states_.as_span().slice(range)) {
+ this->destruct_node_state(item.node, *item.state);
+ }
+ });
+ }
+
+ void destruct_node_state(const DNode node, NodeState &node_state)
+ {
+ /* Need to destruct stuff manually, because it's allocated by a custom allocator. */
+ for (const int i : node->inputs().index_range()) {
+ InputState &input_state = node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ continue;
+ }
+ const InputSocketRef &socket_ref = node->input(i);
+ if (socket_ref.is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ for (MultiInputValueItem &item : multi_value.items) {
+ input_state.type->destruct(item.value);
+ }
+ multi_value.~MultiInputValue();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+ if (value != nullptr) {
+ input_state.type->destruct(value);
+ }
+ single_value.~SingleInputValue();
+ }
+ }
+
+ destruct_n(node_state.inputs.data(), node_state.inputs.size());
+ destruct_n(node_state.outputs.data(), node_state.outputs.size());
+
+ node_state.~NodeState();
+ }
+
+ void forward_group_inputs()
+ {
+ for (auto &&item : params_.input_values.items()) {
+ const DOutputSocket socket = item.key;
+ GMutablePointer value = item.value;
+ this->log_socket_value(socket, value);
+
+ const DNode node = socket.node();
+ if (!node_states_.contains_as(node)) {
+ /* The socket is not connected to any output. */
+ value.destruct();
+ continue;
+ }
+ this->forward_output(socket, value);
+ }
+ }
+
+ void schedule_initial_nodes()
+ {
+ for (const DInputSocket &socket : params_.output_sockets) {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ LockedNode locked_node{*this, node, node_state};
+ /* Setting an input as required will schedule any linked node. */
+ this->set_input_required(locked_node, socket);
+ }
+ }
+
+ void schedule_node(LockedNode &locked_node)
+ {
+ switch (locked_node.node_state.schedule_state) {
+ case NodeScheduleState::NotScheduled: {
+ /* The node will be scheduled once it is not locked anymore. We could schedule the node
+ * right here, but that would result in a deadlock if the task pool decides to run the task
+ * immediately (this only happens when Blender is started with a single thread). */
+ locked_node.node_state.schedule_state = NodeScheduleState::Scheduled;
+ locked_node.delayed_scheduled_nodes.append(locked_node.node);
+ break;
+ }
+ case NodeScheduleState::Scheduled: {
+ /* Scheduled already, nothing to do. */
+ break;
+ }
+ case NodeScheduleState::Running: {
+ /* Reschedule node while it is running.
+ * The node will reschedule itself when it is done. */
+ locked_node.node_state.schedule_state = NodeScheduleState::RunningAndRescheduled;
+ break;
+ }
+ case NodeScheduleState::RunningAndRescheduled: {
+ /* Scheduled already, nothing to do. */
+ break;
+ }
+ }
+ }
+
+ static void run_node_from_task_pool(TaskPool *task_pool, void *task_data)
+ {
+ void *user_data = BLI_task_pool_user_data(task_pool);
+ GeometryNodesEvaluator &evaluator = *(GeometryNodesEvaluator *)user_data;
+ const NodeWithState *node_with_state = (const NodeWithState *)task_data;
+
+ evaluator.node_task_run(node_with_state->node, *node_with_state->state);
+ }
+
+ void node_task_run(const DNode node, NodeState &node_state)
+ {
+ /* These nodes are sometimes scheduled. We could also check for them in other places, but
+ * it's the easiest to do it here. */
+ if (node->is_group_input_node() || node->is_group_output_node()) {
+ return;
+ }
+
+ const bool do_execute_node = this->node_task_preprocessing(node, node_state);
+
+ /* Only execute the node if all prerequisites are met. There has to be an output that is
+ * required and all required inputs have to be provided already. */
+ if (do_execute_node) {
+ this->execute_node(node, node_state);
+ }
+
+ this->node_task_postprocessing(node, node_state);
+ }
+
+ bool node_task_preprocessing(const DNode node, NodeState &node_state)
+ {
+ LockedNode locked_node{*this, node, node_state};
+ BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled);
+ node_state.schedule_state = NodeScheduleState::Running;
+
+ /* Early return if the node has finished already. */
+ if (locked_node.node_state.node_has_finished) {
+ return false;
+ }
+ /* Prepare outputs and check if actually any new outputs have to be computed. */
+ if (!this->prepare_node_outputs_for_execution(locked_node)) {
+ return false;
+ }
+ /* Initialize nodes that don't support laziness. This is done after at least one output is
+ * required and before we check that all required inputs are provided. This reduces the
+ * number of "round-trips" through the task pool by one for most nodes. */
+ if (!node_state.non_lazy_node_is_initialized && !node_supports_laziness(node)) {
+ this->initialize_non_lazy_node(locked_node);
+ node_state.non_lazy_node_is_initialized = true;
+ }
+ /* Prepare inputs and check if all required inputs are provided. */
+ if (!this->prepare_node_inputs_for_execution(locked_node)) {
+ return false;
+ }
+ return true;
+ }
+
+ /* A node is finished when it has computed all outputs that may be used. */
+ bool finish_node_if_possible(LockedNode &locked_node)
+ {
+ if (locked_node.node_state.node_has_finished) {
+ /* Early return in case this node is known to have finished already. */
+ return true;
+ }
+
+ /* Check if there is any output that might be used but has not been computed yet. */
+ bool has_remaining_output = false;
+ for (OutputState &output_state : locked_node.node_state.outputs) {
+ if (output_state.has_been_computed) {
+ continue;
+ }
+ if (output_state.output_usage != ValueUsage::Unused) {
+ has_remaining_output = true;
+ break;
+ }
+ }
+ if (!has_remaining_output) {
+ /* If there are no remaining outputs, all the inputs can be destructed and/or can become
+ * unused. This can also trigger a chain reaction where nodes to the left become finished
+ * too. */
+ for (const int i : locked_node.node->inputs().index_range()) {
+ const DInputSocket socket = locked_node.node.input(i);
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.usage == ValueUsage::Maybe) {
+ this->set_input_unused(locked_node, socket);
+ }
+ else if (input_state.usage == ValueUsage::Required) {
+ /* The value was required, so it cannot become unused. However, we can destruct the
+ * value. */
+ this->destruct_input_value_if_exists(locked_node, socket);
+ }
+ }
+ locked_node.node_state.node_has_finished = true;
+ }
+ return locked_node.node_state.node_has_finished;
+ }
+
+ bool prepare_node_outputs_for_execution(LockedNode &locked_node)
+ {
+ bool execution_is_necessary = false;
+ for (OutputState &output_state : locked_node.node_state.outputs) {
+ /* Update the output usage for execution to the latest value. */
+ output_state.output_usage_for_execution = output_state.output_usage;
+ if (!output_state.has_been_computed) {
+ if (output_state.output_usage == ValueUsage::Required) {
+ /* Only evaluate when there is an output that is required but has not been computed. */
+ execution_is_necessary = true;
+ }
+ }
+ }
+ return execution_is_necessary;
+ }
+
+ void initialize_non_lazy_node(LockedNode &locked_node)
+ {
+ for (const int i : locked_node.node->inputs().index_range()) {
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ /* Ignore unavailable/non-data sockets. */
+ continue;
+ }
+ /* Nodes that don't support laziness require all inputs. */
+ const DInputSocket input_socket = locked_node.node.input(i);
+ this->set_input_required(locked_node, input_socket);
+ }
+ }
+
+ /**
+ * Checks if requested inputs are available and "marks" all the inputs that are available
+ * during the node execution. Inputs that are provided after this function ends but before the
+ * node is executed, cannot be read by the node in the execution (note that this only affects
+ * nodes that support lazy inputs).
+ */
+ bool prepare_node_inputs_for_execution(LockedNode &locked_node)
+ {
+ for (const int i : locked_node.node_state.inputs.index_range()) {
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ /* Ignore unavailable and non-data sockets. */
+ continue;
+ }
+ const DInputSocket socket = locked_node.node.input(i);
+ const bool is_required = input_state.usage == ValueUsage::Required;
+
+ /* No need to check this socket again. */
+ if (input_state.was_ready_for_execution) {
+ continue;
+ }
+
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ /* Checks if all the linked sockets have been provided already. */
+ if (multi_value.items.size() == multi_value.expected_size) {
+ input_state.was_ready_for_execution = true;
+ this->log_socket_value(socket, input_state, multi_value.items);
+ }
+ else if (is_required) {
+ /* The input is required but is not fully provided yet. Therefore the node cannot be
+ * executed yet. */
+ return false;
+ }
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value != nullptr) {
+ input_state.was_ready_for_execution = true;
+ this->log_socket_value(socket, GPointer{input_state.type, single_value.value});
+ }
+ else if (is_required) {
+ /* The input is required but has not been provided yet. Therefore the node cannot be
+ * executed yet. */
+ return false;
+ }
+ }
+ }
+ /* All required inputs have been provided. */
+ return true;
+ }
+
+ /**
+ * Actually execute the node. All the required inputs are available and at least one output is
+ * required.
+ */
+ void execute_node(const DNode node, NodeState &node_state)
+ {
+ const bNode &bnode = *node->bnode();
+
+ if (node_state.has_been_executed) {
+ if (!node_supports_laziness(node)) {
+ /* Nodes that don't support laziness must not be executed more than once. */
+ BLI_assert_unreachable();
+ }
+ }
+ node_state.has_been_executed = true;
+
+ /* Use the geometry node execute callback if it exists. */
+ if (bnode.typeinfo->geometry_node_execute != nullptr) {
+ this->execute_geometry_node(node, node_state);
+ return;
+ }
+
+ /* Use the multi-function implementation if it exists. */
+ const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr);
+ if (multi_function != nullptr) {
+ this->execute_multi_function_node(node, *multi_function, node_state);
+ return;
+ }
+
+ this->execute_unknown_node(node, node_state);
+ }
+
+ void execute_geometry_node(const DNode node, NodeState &node_state)
+ {
+ const bNode &bnode = *node->bnode();
+
+ NodeParamsProvider params_provider{*this, node, node_state};
+ GeoNodeExecParams params{params_provider};
+ bnode.typeinfo->geometry_node_execute(params);
+ }
+
+ void execute_multi_function_node(const DNode node,
+ const MultiFunction &fn,
+ NodeState &node_state)
+ {
+ MFContextBuilder fn_context;
+ MFParamsBuilder fn_params{fn, 1};
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ /* Prepare the inputs for the multi function. */
+ for (const int i : node->inputs().index_range()) {
+ const InputSocketRef &socket_ref = node->input(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ BLI_assert(!socket_ref.is_multi_input_socket());
+ InputState &input_state = node_state.inputs[i];
+ BLI_assert(input_state.was_ready_for_execution);
+ SingleInputValue &single_value = *input_state.value.single;
+ BLI_assert(single_value.value != nullptr);
+ fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value});
+ }
+ /* Prepare the outputs for the multi function. */
+ Vector<GMutablePointer> outputs;
+ for (const int i : node->outputs().index_range()) {
+ const OutputSocketRef &socket_ref = node->output(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ const CPPType &type = *get_socket_cpp_type(socket_ref);
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1});
+ outputs.append({type, buffer});
+ }
+
+ fn.call(IndexRange(1), fn_params, fn_context);
+
+ /* Forward the computed outputs. */
+ int output_index = 0;
+ for (const int i : node->outputs().index_range()) {
+ const OutputSocketRef &socket_ref = node->output(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ OutputState &output_state = node_state.outputs[i];
+ const DOutputSocket socket{node.context(), &socket_ref};
+ GMutablePointer value = outputs[output_index];
+ this->forward_output(socket, value);
+ output_state.has_been_computed = true;
+ output_index++;
+ }
+ }
+
+ void execute_unknown_node(const DNode node, NodeState &node_state)
+ {
+ LinearAllocator<> &allocator = local_allocators_.local();
+ for (const OutputSocketRef *socket : node->outputs()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(*socket);
+ if (type == nullptr) {
+ continue;
+ }
+ /* Just forward the default value of the type as a fallback. That's typically better than
+ * crashing or doing nothing. */
+ OutputState &output_state = node_state.outputs[socket->index()];
+ output_state.has_been_computed = true;
+ void *buffer = allocator.allocate(type->size(), type->alignment());
+ type->copy_to_uninitialized(type->default_value(), buffer);
+ this->forward_output({node.context(), socket}, {*type, buffer});
+ }
+ }
+
+ void node_task_postprocessing(const DNode node, NodeState &node_state)
+ {
+ LockedNode locked_node{*this, node, node_state};
+
+ const bool node_has_finished = this->finish_node_if_possible(locked_node);
+ const bool reschedule_requested = node_state.schedule_state ==
+ NodeScheduleState::RunningAndRescheduled;
+ node_state.schedule_state = NodeScheduleState::NotScheduled;
+ if (reschedule_requested && !node_has_finished) {
+ /* Either the node rescheduled itself or another node tried to schedule it while it ran. */
+ this->schedule_node(locked_node);
+ }
+
+ this->assert_expected_outputs_have_been_computed(locked_node);
+ }
+
+ void assert_expected_outputs_have_been_computed(LockedNode &locked_node)
+ {
+#ifdef DEBUG
+ /* Outputs can only be computed when all required inputs have been provided. */
+ if (locked_node.node_state.missing_required_inputs > 0) {
+ return;
+ }
+ /* If the node is still scheduled, it is not necessary that all its expected outputs are
+ * computed yet. */
+ if (locked_node.node_state.schedule_state == NodeScheduleState::Scheduled) {
+ return;
+ }
+
+ const bool supports_laziness = node_supports_laziness(locked_node.node);
+ /* Iterating over sockets instead of the states directly, because that makes it easier to
+ * figure out which socket is missing when one of the asserts is hit. */
+ for (const OutputSocketRef *socket_ref : locked_node.node->outputs()) {
+ OutputState &output_state = locked_node.node_state.outputs[socket_ref->index()];
+ if (supports_laziness) {
+ /* Expected that at least all required sockets have been computed. If more outputs become
+ * required later, the node will be executed again. */
+ if (output_state.output_usage_for_execution == ValueUsage::Required) {
+ BLI_assert(output_state.has_been_computed);
+ }
+ }
+ else {
+ /* Expect that all outputs that may be used have been computed, because the node cannot
+ * be executed again. */
+ if (output_state.output_usage_for_execution != ValueUsage::Unused) {
+ BLI_assert(output_state.has_been_computed);
+ }
+ }
+ }
+#else
+ UNUSED_VARS(locked_node);
+#endif
+ }
+
+ void extract_group_outputs()
+ {
+ for (const DInputSocket &socket : params_.output_sockets) {
+ BLI_assert(socket->is_available());
+ BLI_assert(!socket->is_multi_input_socket());
+
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ InputState &input_state = node_state.inputs[socket->index()];
+
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+
+ /* The value should have been computed by now. If this assert is hit, it means that there
+ * was some scheduling issue before. */
+ BLI_assert(value != nullptr);
+
+ /* Move value into memory owned by the outer allocator. */
+ const CPPType &type = *input_state.type;
+ void *buffer = outer_allocator_.allocate(type.size(), type.alignment());
+ type.move_to_uninitialized(value, buffer);
+
+ params_.r_output_values.append({type, buffer});
+ }
+ }
+
+ /**
+ * Load the required input from the socket or trigger nodes to the left to compute the value.
+ * When this function is called, the node will always be executed again eventually (either
+ * immediately, or when all required inputs have been computed by other nodes).
+ */
+ void set_input_required(LockedNode &locked_node, const DInputSocket input_socket)
+ {
+ BLI_assert(locked_node.node == input_socket.node());
+ InputState &input_state = locked_node.node_state.inputs[input_socket->index()];
+
+ /* Value set as unused cannot become used again. */
+ BLI_assert(input_state.usage != ValueUsage::Unused);
+
+ if (input_state.usage == ValueUsage::Required) {
+ /* The value is already required, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ /* Returning here also ensure that the code below is executed at most once per input. */
+ return;
+ }
+ input_state.usage = ValueUsage::Required;
+
+ if (input_state.was_ready_for_execution) {
+ /* The value was already ready, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ return;
+ }
+
+ /* Count how many values still have to be added to this input until it is "complete". */
+ int missing_values = 0;
+ if (input_socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ missing_values = multi_value.expected_size - multi_value.items.size();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value == nullptr) {
+ missing_values = 1;
+ }
+ }
+ if (missing_values == 0) {
+ /* The input is fully available already, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ return;
+ }
+ /* Increase the total number of missing required inputs. This ensures that the node will be
+ * scheduled correctly when all inputs have been provided. */
+ locked_node.node_state.missing_required_inputs += missing_values;
+
+ /* Get all origin sockets, because we have to tag those as required as well. */
+ Vector<DSocket> origin_sockets;
+ input_socket.foreach_origin_socket(
+ [&](const DSocket origin_socket) { origin_sockets.append(origin_socket); });
+
+ if (origin_sockets.is_empty()) {
+ /* If there are no origin sockets, just load the value from the socket directly. */
+ this->load_unlinked_input_value(locked_node, input_socket, input_state, input_socket);
+ locked_node.node_state.missing_required_inputs -= 1;
+ this->schedule_node(locked_node);
+ return;
+ }
+ bool will_be_triggered_by_other_node = false;
+ for (const DSocket origin_socket : origin_sockets) {
+ if (origin_socket->is_input()) {
+ /* Load the value directly from the origin socket. In most cases this is an unlinked
+ * group input. */
+ this->load_unlinked_input_value(locked_node, input_socket, input_state, origin_socket);
+ locked_node.node_state.missing_required_inputs -= 1;
+ this->schedule_node(locked_node);
+ return;
+ }
+ /* The value has not been computed yet, so when it will be forwarded by another node, this
+ * node will be triggered. */
+ will_be_triggered_by_other_node = true;
+
+ locked_node.delayed_required_outputs.append(DOutputSocket(origin_socket));
+ }
+ /* If this node will be triggered by another node, we don't have to schedule it now. */
+ if (!will_be_triggered_by_other_node) {
+ this->schedule_node(locked_node);
+ }
+ }
+
+ void set_input_unused(LockedNode &locked_node, const DInputSocket socket)
+ {
+ InputState &input_state = locked_node.node_state.inputs[socket->index()];
+
+ /* A required socket cannot become unused. */
+ BLI_assert(input_state.usage != ValueUsage::Required);
+
+ if (input_state.usage == ValueUsage::Unused) {
+ /* Nothing to do in this case. */
+ return;
+ }
+ input_state.usage = ValueUsage::Unused;
+
+ /* If the input is unused, it's value can be destructed now. */
+ this->destruct_input_value_if_exists(locked_node, socket);
+
+ if (input_state.was_ready_for_execution) {
+ /* If the value was already computed, we don't need to notify origin nodes. */
+ return;
+ }
+
+ /* Notify origin nodes that might want to set its inputs as unused as well. */
+ socket.foreach_origin_socket([&](const DSocket origin_socket) {
+ if (origin_socket->is_input()) {
+ /* Values from these sockets are loaded directly from the sockets, so there is no node to
+ * notify. */
+ return;
+ }
+ /* Delay notification of the other node until this node is not locked anymore. */
+ locked_node.delayed_unused_outputs.append(DOutputSocket(origin_socket));
+ });
+ }
+
+ void send_output_required_notification(const DOutputSocket socket)
+ {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ OutputState &output_state = node_state.outputs[socket->index()];
+
+ LockedNode locked_node{*this, node, node_state};
+ if (output_state.output_usage == ValueUsage::Required) {
+ /* Output is marked as required already. So the node is scheduled already. */
+ return;
+ }
+ /* The origin node needs to be scheduled so that it provides the requested input
+ * eventually. */
+ output_state.output_usage = ValueUsage::Required;
+ this->schedule_node(locked_node);
+ }
+
+ void send_output_unused_notification(const DOutputSocket socket)
+ {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ OutputState &output_state = node_state.outputs[socket->index()];
+
+ LockedNode locked_node{*this, node, node_state};
+ output_state.potential_users -= 1;
+ if (output_state.potential_users == 0) {
+ /* The output socket has no users anymore. */
+ output_state.output_usage = ValueUsage::Unused;
+ /* Schedule the origin node in case it wants to set its inputs as unused as well. */
+ this->schedule_node(locked_node);
+ }
+ }
+
+ void add_node_to_task_pool(const DNode node)
+ {
+ /* Push the task to the pool while it is not locked to avoid a deadlock in case when the task
+ * is executed immediately. */
+ const NodeWithState *node_with_state = node_states_.lookup_key_ptr_as(node);
+ BLI_task_pool_push(
+ task_pool_, run_node_from_task_pool, (void *)node_with_state, false, nullptr);
+ }
+
+ /**
+ * Moves a newly computed value from an output socket to all the inputs that might need it.
+ */
+ void forward_output(const DOutputSocket from_socket, GMutablePointer value_to_forward)
+ {
+ BLI_assert(value_to_forward.get() != nullptr);
+
+ Vector<DInputSocket> to_sockets;
+ auto handle_target_socket_fn = [&, this](const DInputSocket to_socket) {
+ if (this->should_forward_to_socket(to_socket)) {
+ to_sockets.append(to_socket);
+ }
+ };
+ auto handle_skipped_socket_fn = [&, this](const DSocket socket) {
+ /* Log socket value on intermediate sockets to support e.g. attribute search or spreadsheet
+ * breadcrumbs on group nodes. */
+ this->log_socket_value(socket, value_to_forward);
+ };
+ from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn);
+
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ const CPPType &from_type = *value_to_forward.type();
+ Vector<DInputSocket> to_sockets_same_type;
+ for (const DInputSocket &to_socket : to_sockets) {
+ const CPPType &to_type = *get_socket_cpp_type(to_socket);
+ if (from_type == to_type) {
+ /* All target sockets that do not need a conversion will be handled afterwards. */
+ to_sockets_same_type.append(to_socket);
+ continue;
+ }
+ this->forward_to_socket_with_different_type(
+ allocator, value_to_forward, from_socket, to_socket, to_type);
+ }
+ this->forward_to_sockets_with_same_type(
+ allocator, to_sockets_same_type, value_to_forward, from_socket);
+ }
+
+ bool should_forward_to_socket(const DInputSocket socket)
+ {
+ const DNode to_node = socket.node();
+ const NodeWithState *target_node_with_state = node_states_.lookup_key_ptr_as(to_node);
+ if (target_node_with_state == nullptr) {
+ /* If the socket belongs to a node that has no state, the entire node is not used. */
+ return false;
+ }
+ NodeState &target_node_state = *target_node_with_state->state;
+ InputState &target_input_state = target_node_state.inputs[socket->index()];
+
+ std::lock_guard lock{target_node_state.mutex};
+ /* Do not forward to an input socket whose value won't be used. */
+ return target_input_state.usage != ValueUsage::Unused;
+ }
+
+ void forward_to_socket_with_different_type(LinearAllocator<> &allocator,
+ const GPointer value_to_forward,
+ const DOutputSocket from_socket,
+ const DInputSocket to_socket,
+ const CPPType &to_type)
+ {
+ const CPPType &from_type = *value_to_forward.type();
+
+ /* Allocate a buffer for the converted value. */
+ void *buffer = allocator.allocate(to_type.size(), to_type.alignment());
+
+ if (conversions_.is_convertible(from_type, to_type)) {
+ /* Do the conversion if possible. */
+ conversions_.convert_to_uninitialized(from_type, to_type, value_to_forward.get(), buffer);
+ }
+ else {
+ /* Cannot convert, use default value instead. */
+ to_type.copy_to_uninitialized(to_type.default_value(), buffer);
+ }
+ this->add_value_to_input_socket(to_socket, from_socket, {to_type, buffer});
+ }
+
+ void forward_to_sockets_with_same_type(LinearAllocator<> &allocator,
+ Span<DInputSocket> to_sockets,
+ GMutablePointer value_to_forward,
+ const DOutputSocket from_socket)
+ {
+ if (to_sockets.is_empty()) {
+ /* Value is not used anymore, so it can be destructed. */
+ value_to_forward.destruct();
+ }
+ else if (to_sockets.size() == 1) {
+ /* Value is only used by one input socket, no need to copy it. */
+ const DInputSocket to_socket = to_sockets[0];
+ this->add_value_to_input_socket(to_socket, from_socket, value_to_forward);
+ }
+ else {
+ /* Multiple inputs use the value, make a copy for every input except for one. */
+ /* First make the copies, so that the next node does not start modifying the value while we
+ * are still making copies. */
+ const CPPType &type = *value_to_forward.type();
+ for (const DInputSocket &to_socket : to_sockets.drop_front(1)) {
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(value_to_forward.get(), buffer);
+ this->add_value_to_input_socket(to_socket, from_socket, {type, buffer});
+ }
+ /* Forward the original value to one of the targets. */
+ const DInputSocket to_socket = to_sockets[0];
+ this->add_value_to_input_socket(to_socket, from_socket, value_to_forward);
+ }
+ }
+
+ void add_value_to_input_socket(const DInputSocket socket,
+ const DOutputSocket origin,
+ GMutablePointer value)
+ {
+ BLI_assert(socket->is_available());
+
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ InputState &input_state = node_state.inputs[socket->index()];
+
+ /* Lock the node because we want to change its state. */
+ LockedNode locked_node{*this, node, node_state};
+
+ if (socket->is_multi_input_socket()) {
+ /* Add a new value to the multi-input. */
+ MultiInputValue &multi_value = *input_state.value.multi;
+ multi_value.items.append({origin, value.get()});
+ }
+ else {
+ /* Assign the value to the input. */
+ SingleInputValue &single_value = *input_state.value.single;
+ BLI_assert(single_value.value == nullptr);
+ single_value.value = value.get();
+ }
+
+ if (input_state.usage == ValueUsage::Required) {
+ node_state.missing_required_inputs--;
+ if (node_state.missing_required_inputs == 0) {
+ /* Schedule node if all the required inputs have been provided. */
+ this->schedule_node(locked_node);
+ }
+ }
+ }
+
+ void load_unlinked_input_value(LockedNode &locked_node,
+ const DInputSocket input_socket,
+ InputState &input_state,
+ const DSocket origin_socket)
+ {
+ /* Only takes locked node as parameter, because the node needs to be locked. */
+ UNUSED_VARS(locked_node);
+
+ GMutablePointer value = this->get_value_from_socket(origin_socket, *input_state.type);
+ if (input_socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ multi_value.items.append({origin_socket, value.get()});
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ single_value.value = value.get();
+ }
+ }
+
+ void destruct_input_value_if_exists(LockedNode &locked_node, const DInputSocket socket)
+ {
+ InputState &input_state = locked_node.node_state.inputs[socket->index()];
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ for (MultiInputValueItem &item : multi_value.items) {
+ input_state.type->destruct(item.value);
+ }
+ multi_value.items.clear();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value != nullptr) {
+ input_state.type->destruct(single_value.value);
+ single_value.value = nullptr;
+ }
+ }
+ }
+
+ GMutablePointer get_value_from_socket(const DSocket socket, const CPPType &required_type)
+ {
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ bNodeSocket *bsocket = socket->bsocket();
+ const CPPType &type = *get_socket_cpp_type(socket);
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ blender::nodes::socket_cpp_value_get(*bsocket, buffer);
+
+ if (type == required_type) {
+ return {type, buffer};
+ }
+ if (conversions_.is_convertible(type, required_type)) {
+ /* Convert the loaded value to the required type if possible. */
+ void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment());
+ conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer);
+ type.destruct(buffer);
+ return {required_type, converted_buffer};
+ }
+ /* Use a default fallback value when the loaded type is not compatible. */
+ void *default_buffer = allocator.allocate(required_type.size(), required_type.alignment());
+ required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
+ return {required_type, default_buffer};
+ }
+
+ NodeState &get_node_state(const DNode node)
+ {
+ return *node_states_.lookup_key_as(node).state;
+ }
+
+ void log_socket_value(const DSocket socket, Span<GPointer> values)
+ {
+ if (params_.log_socket_value_fn) {
+ params_.log_socket_value_fn(socket, values);
+ }
+ }
+
+ void log_socket_value(const DSocket socket,
+ InputState &input_state,
+ Span<MultiInputValueItem> values)
+ {
+ Vector<GPointer, 16> value_pointers;
+ value_pointers.reserve(values.size());
+ const CPPType &type = *input_state.type;
+ for (const MultiInputValueItem &item : values) {
+ value_pointers.append({type, item.value});
+ }
+ this->log_socket_value(socket, value_pointers);
+ }
+
+ void log_socket_value(const DSocket socket, GPointer value)
+ {
+ this->log_socket_value(socket, Span<GPointer>(&value, 1));
+ }
+};
+
+LockedNode::~LockedNode()
+{
+ /* First unlock the current node. */
+ node_state.mutex.unlock();
+ /* Then send notifications to the other nodes. */
+ for (const DOutputSocket &socket : delayed_required_outputs) {
+ evaluator_.send_output_required_notification(socket);
+ }
+ for (const DOutputSocket &socket : delayed_unused_outputs) {
+ evaluator_.send_output_unused_notification(socket);
+ }
+ for (const DNode &node : delayed_scheduled_nodes) {
+ evaluator_.add_node_to_task_pool(node);
+ }
+}
+
+NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator,
+ DNode dnode,
+ NodeState &node_state)
+ : evaluator_(evaluator), node_state_(node_state)
+{
+ this->dnode = dnode;
+ this->self_object = evaluator.params_.self_object;
+ this->modifier = &evaluator.params_.modifier_->modifier;
+ this->depsgraph = evaluator.params_.depsgraph;
+}
+
+bool NodeParamsProvider::can_get_input(StringRef identifier) const
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ if (!input_state.was_ready_for_execution) {
+ return false;
+ }
+
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ return multi_value.items.size() == multi_value.expected_size;
+ }
+ SingleInputValue &single_value = *input_state.value.single;
+ return single_value.value != nullptr;
+}
+
+bool NodeParamsProvider::can_set_output(StringRef identifier) const
+{
+ const DOutputSocket socket = this->dnode.output_by_identifier(identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ return !output_state.has_been_computed;
+}
+
+GMutablePointer NodeParamsProvider::extract_input(StringRef identifier)
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+ BLI_assert(!socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+ single_value.value = nullptr;
+ return {*input_state.type, value};
+}
+
+Vector<GMutablePointer> NodeParamsProvider::extract_multi_input(StringRef identifier)
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+ BLI_assert(socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ MultiInputValue &multi_value = *input_state.value.multi;
+
+ Vector<GMutablePointer> ret_values;
+ socket.foreach_origin_socket([&](DSocket origin) {
+ for (const MultiInputValueItem &item : multi_value.items) {
+ if (item.origin == origin) {
+ ret_values.append({*input_state.type, item.value});
+ return;
+ }
+ }
+ BLI_assert_unreachable();
+ });
+ if (ret_values.is_empty()) {
+ /* If the socket is not linked, we just use the value from the socket itself. */
+ BLI_assert(multi_value.items.size() == 1);
+ MultiInputValueItem &item = multi_value.items[0];
+ BLI_assert(item.origin == socket);
+ ret_values.append({*input_state.type, item.value});
+ }
+ multi_value.items.clear();
+ return ret_values;
+}
+
+GPointer NodeParamsProvider::get_input(StringRef identifier) const
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+ BLI_assert(!socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ SingleInputValue &single_value = *input_state.value.single;
+ return {*input_state.type, single_value.value};
+}
+
+GMutablePointer NodeParamsProvider::alloc_output_value(const CPPType &type)
+{
+ LinearAllocator<> &allocator = evaluator_.local_allocators_.local();
+ return {type, allocator.allocate(type.size(), type.alignment())};
+}
+
+void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value)
+{
+ const DOutputSocket socket = this->dnode.output_by_identifier(identifier);
+ BLI_assert(socket);
+
+ evaluator_.log_socket_value(socket, value);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ BLI_assert(!output_state.has_been_computed);
+ evaluator_.forward_output(socket, value);
+ output_state.has_been_computed = true;
+}
+
+bool NodeParamsProvider::lazy_require_input(StringRef identifier)
+{
+ BLI_assert(node_supports_laziness(this->dnode));
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ if (input_state.was_ready_for_execution) {
+ return false;
+ }
+ LockedNode locked_node{evaluator_, this->dnode, node_state_};
+ evaluator_.set_input_required(locked_node, socket);
+ return true;
+}
+
+void NodeParamsProvider::set_input_unused(StringRef identifier)
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+
+ LockedNode locked_node{evaluator_, this->dnode, node_state_};
+ evaluator_.set_input_unused(locked_node, socket);
+}
+
+bool NodeParamsProvider::output_is_required(StringRef identifier) const
+{
+ const DOutputSocket socket = this->dnode.output_by_identifier(identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ if (output_state.has_been_computed) {
+ return false;
+ }
+ return output_state.output_usage_for_execution != ValueUsage::Unused;
+}
+
+bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const
+{
+ BLI_assert(node_supports_laziness(this->dnode));
+ const DOutputSocket socket = this->dnode.output_by_identifier(identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ if (output_state.has_been_computed) {
+ return false;
+ }
+ return output_state.output_usage_for_execution == ValueUsage::Required;
+}
+
+void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params)
+{
+ GeometryNodesEvaluator evaluator{params};
+ evaluator.execute();
+}
+
+} // namespace blender::modifiers::geometry_nodes
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
new file mode 100644
index 00000000000..84249e4244e
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
@@ -0,0 +1,52 @@
+/*
+ * 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_map.hh"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_tree_multi_function.hh"
+
+#include "FN_generic_pointer.hh"
+
+#include "DNA_modifier_types.h"
+
+namespace blender::modifiers::geometry_nodes {
+
+using namespace nodes::derived_node_tree_types;
+using fn::GMutablePointer;
+using fn::GPointer;
+
+using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>;
+
+struct GeometryNodesEvaluationParams {
+ blender::LinearAllocator<> allocator;
+
+ Map<DOutputSocket, GMutablePointer> input_values;
+ Vector<DInputSocket> output_sockets;
+ nodes::MultiFunctionByNode *mf_by_node;
+ const NodesModifierData *modifier_;
+ Depsgraph *depsgraph;
+ Object *self_object;
+ LogSocketValueFn log_socket_value_fn;
+
+ Vector<GMutablePointer> r_output_values;
+};
+
+void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params);
+
+} // namespace blender::modifiers::geometry_nodes
diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c
index 4daa527577b..a01f63be791 100644
--- a/source/blender/modifiers/intern/MOD_none.c
+++ b/source/blender/modifiers/intern/MOD_none.c
@@ -60,7 +60,6 @@ ModifierTypeInfo modifierType_None = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index ec10b18665e..4bfd6aba4b2 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -805,7 +805,6 @@ ModifierTypeInfo modifierType_NormalEdit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index 9c940745920..f7ac59f9e4b 100644
--- a/source/blender/modifiers/intern/MOD_ocean.c
+++ b/source/blender/modifiers/intern/MOD_ocean.c
@@ -737,7 +737,6 @@ ModifierTypeInfo modifierType_Ocean = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index e7f1fa9943e..60c5667472e 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -679,7 +679,6 @@ ModifierTypeInfo modifierType_ParticleInstance = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index 4c1179af431..38cce5e6a50 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -337,7 +337,6 @@ ModifierTypeInfo modifierType_ParticleSystem = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index 175435fcd44..88851f91337 100644
--- a/source/blender/modifiers/intern/MOD_remesh.c
+++ b/source/blender/modifiers/intern/MOD_remesh.c
@@ -301,7 +301,6 @@ ModifierTypeInfo modifierType_Remesh = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index 84360caa345..b236e0896b7 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -1258,7 +1258,6 @@ ModifierTypeInfo modifierType_Screw = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c
index 81a0ee72496..b517bc102f8 100644
--- a/source/blender/modifiers/intern/MOD_shapekey.c
+++ b/source/blender/modifiers/intern/MOD_shapekey.c
@@ -141,7 +141,6 @@ ModifierTypeInfo modifierType_ShapeKey = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index 93626309727..a12724ec23c 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -293,7 +293,6 @@ ModifierTypeInfo modifierType_Shrinkwrap = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index ea31bdc6e31..db01dec4d19 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -21,10 +21,9 @@
* \ingroup modifiers
*/
-#include "BLI_utildefines.h"
-
#include "BLI_math.h"
-
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "DNA_defaults.h"
@@ -57,6 +56,21 @@
#define BEND_EPS 0.000001f
+ALIGN_STRUCT struct DeformUserData {
+ bool invert_vgroup;
+ char mode;
+ char deform_axis;
+ int lock_axis;
+ int vgroup;
+ int limit_axis;
+ float weight;
+ float smd_factor;
+ float smd_limit[2];
+ float (*vertexCos)[3];
+ const SpaceTransform *transf;
+ const MDeformVert *dvert;
+};
+
/* Re-maps the indices for X Y Z by shifting them up and wrapping, such that
* X = Y, Y = Z, Z = X (for X axis), and X = Z, Y = X, Z = Y (for Y axis). This
* exists because the deformations (excluding bend) are based on the Z axis.
@@ -170,10 +184,12 @@ static void simpleDeform_bend(const float factor,
sint = sinf(theta);
cost = cosf(theta);
+ /* NOTE: the operations below a susceptible to float precision errors
+ * regarding the order of operations, take care when changing, see: T85470 */
switch (axis) {
case 0:
r_co[0] = x;
- r_co[1] = (y - 1.0f / factor) * cost + 1.0f / factor;
+ r_co[1] = y * cost + (1.0f - cost) / factor;
r_co[2] = -(y - 1.0f / factor) * sint;
{
r_co[0] += dcut[0];
@@ -182,7 +198,7 @@ static void simpleDeform_bend(const float factor,
}
break;
case 1:
- r_co[0] = (x - 1.0f / factor) * cost + 1.0f / factor;
+ r_co[0] = x * cost + (1.0f - cost) / factor;
r_co[1] = y;
r_co[2] = -(x - 1.0f / factor) * sint;
{
@@ -193,7 +209,7 @@ static void simpleDeform_bend(const float factor,
break;
default:
r_co[0] = -(y - 1.0f / factor) * sint;
- r_co[1] = (y - 1.0f / factor) * cost + 1.0f / factor;
+ r_co[1] = y * cost + (1.0f - cost) / factor;
r_co[2] = z;
{
r_co[0] += cost * dcut[0];
@@ -203,6 +219,88 @@ static void simpleDeform_bend(const float factor,
}
}
+static void simple_helper(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ const struct DeformUserData *curr_deform_data = userdata;
+ float weight = BKE_defvert_array_find_weight_safe(
+ curr_deform_data->dvert, iter, curr_deform_data->vgroup);
+ const uint *axis_map = axis_map_table[(curr_deform_data->mode != MOD_SIMPLEDEFORM_MODE_BEND) ?
+ curr_deform_data->deform_axis :
+ 2];
+ const float base_limit[2] = {0.0f, 0.0f};
+
+ if (curr_deform_data->invert_vgroup) {
+ weight = 1.0f - weight;
+ }
+
+ if (weight != 0.0f) {
+ float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};
+
+ if (curr_deform_data->transf) {
+ BLI_space_transform_apply(curr_deform_data->transf, curr_deform_data->vertexCos[iter]);
+ }
+
+ copy_v3_v3(co, curr_deform_data->vertexCos[iter]);
+
+ /* Apply axis limits, and axis mappings */
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) {
+ axis_limit(0, base_limit, co, dcut);
+ }
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) {
+ axis_limit(1, base_limit, co, dcut);
+ }
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) {
+ axis_limit(2, base_limit, co, dcut);
+ }
+ axis_limit(curr_deform_data->limit_axis, curr_deform_data->smd_limit, co, dcut);
+
+ /* apply the deform to a mapped copy of the vertex, and then re-map it back. */
+ float co_remap[3];
+ float dcut_remap[3];
+ copy_v3_v3_map(co_remap, co, axis_map);
+ copy_v3_v3_map(dcut_remap, dcut, axis_map);
+ switch (curr_deform_data->mode) {
+ case MOD_SIMPLEDEFORM_MODE_TWIST:
+ simpleDeform_twist(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_BEND:
+ simpleDeform_bend(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_TAPER:
+ simpleDeform_taper(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_STRETCH:
+ simpleDeform_stretch(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ default:
+ return; /* No simple-deform mode? */
+ }
+ copy_v3_v3_unmap(co, co_remap, axis_map);
+
+ /* Use vertex weight has coef of linear interpolation */
+ interp_v3_v3v3(
+ curr_deform_data->vertexCos[iter], curr_deform_data->vertexCos[iter], co, weight);
+
+ if (curr_deform_data->transf) {
+ BLI_space_transform_invert(curr_deform_data->transf, curr_deform_data->vertexCos[iter]);
+ }
+ }
+}
+
/* simple deform modifier */
static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
const ModifierEvalContext *UNUSED(ctx),
@@ -211,14 +309,9 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
float (*vertexCos)[3],
int numVerts)
{
- const float base_limit[2] = {0.0f, 0.0f};
int i;
float smd_limit[2], smd_factor;
SpaceTransform *transf = NULL, tmp_transf;
- void (*simpleDeform_callback)(const float factor,
- const int axis,
- const float dcut[3],
- float co[3]) = NULL; /* Mode callback */
int vgroup;
MDeformVert *dvert;
@@ -300,23 +393,6 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]);
}
- switch (smd->mode) {
- case MOD_SIMPLEDEFORM_MODE_TWIST:
- simpleDeform_callback = simpleDeform_twist;
- break;
- case MOD_SIMPLEDEFORM_MODE_BEND:
- simpleDeform_callback = simpleDeform_bend;
- break;
- case MOD_SIMPLEDEFORM_MODE_TAPER:
- simpleDeform_callback = simpleDeform_taper;
- break;
- case MOD_SIMPLEDEFORM_MODE_STRETCH:
- simpleDeform_callback = simpleDeform_stretch;
- break;
- default:
- return; /* No simple-deform mode? */
- }
-
if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) {
if (fabsf(smd_factor) < BEND_EPS) {
return;
@@ -325,53 +401,26 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
MOD_get_vgroup(ob, mesh, smd->vgroup_name, &dvert, &vgroup);
const bool invert_vgroup = (smd->flag & MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP) != 0;
- const uint *axis_map =
- axis_map_table[(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? deform_axis : 2];
-
- for (i = 0; i < numVerts; i++) {
- float weight = BKE_defvert_array_find_weight_safe(dvert, i, vgroup);
-
- if (invert_vgroup) {
- weight = 1.0f - weight;
- }
-
- if (weight != 0.0f) {
- float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};
- if (transf) {
- BLI_space_transform_apply(transf, vertexCos[i]);
- }
-
- copy_v3_v3(co, vertexCos[i]);
-
- /* Apply axis limits, and axis mappings */
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) {
- axis_limit(0, base_limit, co, dcut);
- }
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) {
- axis_limit(1, base_limit, co, dcut);
- }
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) {
- axis_limit(2, base_limit, co, dcut);
- }
- axis_limit(limit_axis, smd_limit, co, dcut);
-
- /* apply the deform to a mapped copy of the vertex, and then re-map it back. */
- float co_remap[3];
- float dcut_remap[3];
- copy_v3_v3_map(co_remap, co, axis_map);
- copy_v3_v3_map(dcut_remap, dcut, axis_map);
- simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */
- copy_v3_v3_unmap(co, co_remap, axis_map);
-
- /* Use vertex weight has coef of linear interpolation */
- interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight);
-
- if (transf) {
- BLI_space_transform_invert(transf, vertexCos[i]);
- }
- }
- }
+ /* Build our data. */
+ const struct DeformUserData deform_pool_data = {
+ .mode = smd->mode,
+ .smd_factor = smd_factor,
+ .deform_axis = deform_axis,
+ .transf = transf,
+ .vertexCos = vertexCos,
+ .invert_vgroup = invert_vgroup,
+ .lock_axis = lock_axis,
+ .vgroup = vgroup,
+ .smd_limit[0] = smd_limit[0],
+ .smd_limit[1] = smd_limit[1],
+ .dvert = dvert,
+ .limit_axis = limit_axis,
+ };
+ /* Do deformation. */
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ BLI_task_parallel_range(0, numVerts, (void *)&deform_pool_data, simple_helper, &settings);
}
/* SimpleDeform */
@@ -553,7 +602,6 @@ ModifierTypeInfo modifierType_SimpleDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 5e412185cea..58d70ef3a4a 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -90,6 +90,46 @@
#include "bmesh.h"
+/* -------------------------------------------------------------------- */
+/** \name Generic BMesh Utilities
+ * \{ */
+
+static void vert_face_normal_mark_set(BMVert *v)
+{
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
+ f->no[0] = FLT_MAX;
+ }
+}
+
+static void vert_face_normal_mark_update(BMVert *v)
+{
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
+ if (f->no[0] == FLT_MAX) {
+ BM_face_normal_update(f);
+ }
+ }
+}
+
+/**
+ * Recalculate the normals of all faces connected to `verts`.
+ */
+static void vert_array_face_normal_update(BMVert **verts, int verts_len)
+{
+ for (int i = 0; i < verts_len; i++) {
+ vert_face_normal_mark_set(verts[i]);
+ }
+
+ for (int i = 0; i < verts_len; i++) {
+ vert_face_normal_mark_update(verts[i]);
+ }
+}
+
+/** \} */
+
typedef struct {
float mat[3][3];
/* Vert that edge is pointing away from, no relation to
@@ -1352,13 +1392,25 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f
split_face = collapse_face_corners(bm, split_face, 4, vert_buf);
}
- /* Done with dynamic array, split_face must now be a quad */
- BLI_array_free(vert_buf);
+ /* `split_face` should now be a quad. */
BLI_assert(split_face->len == 4);
+
+ /* Account for the highly unlikely case that it's not a quad. */
if (split_face->len != 4) {
+ /* Reuse `vert_buf` for updating normals. */
+ BLI_array_clear(vert_buf);
+ BLI_array_grow_items(vert_buf, split_face->len);
+
+ BM_iter_as_array(bm, BM_FACES_OF_VERT, split_face, (void **)vert_buf, split_face->len);
+
+ vert_array_face_normal_update(vert_buf, split_face->len);
+ BLI_array_free(vert_buf);
return;
}
+ /* Done with dynamic array. */
+ BLI_array_free(vert_buf);
+
/* Get split face's verts */
// BM_iter_as_array(bm, BM_VERTS_OF_FACE, split_face, (void **)verts, 4);
BM_face_as_array_vert_quad(split_face, verts);
@@ -1373,6 +1425,8 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f
}
BMO_op_exec(bm, &op);
BMO_op_finish(bm, &op);
+
+ vert_array_face_normal_update(frame->verts, 4);
}
/* If the frame has some vertices that are inside the hull (detached)
@@ -1731,6 +1785,11 @@ static void skin_smooth_hulls(BMesh *bm,
/* Done with original coordinates */
BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey);
+
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BM_face_normal_update(f);
+ }
}
/* Returns true if all hulls are successfully built, false otherwise */
@@ -2044,7 +2103,6 @@ ModifierTypeInfo modifierType_Skin = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index c5011ed15c1..97027e2ecff 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -286,7 +286,6 @@ ModifierTypeInfo modifierType_Smooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index 9a657c44fca..d7d2f948955 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -120,7 +120,6 @@ ModifierTypeInfo modifierType_Softbody = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 8e519a72df1..736dd08a713 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -276,7 +276,6 @@ ModifierTypeInfo modifierType_Solidify = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index 99069919120..a77a6e595ad 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -216,7 +216,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
numVerts, sizeof(*old_vert_arr), "old_vert_arr in solidify");
uint *edge_users = NULL;
- char *edge_order = NULL;
+ int *edge_order = NULL;
float(*vert_nors)[3] = NULL;
float(*poly_nors)[3] = NULL;
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index c3611488f5f..4dc45ad0324 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -509,7 +509,6 @@ ModifierTypeInfo modifierType_Subsurf = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index 7416a4baf38..bfd4cd81803 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -184,13 +184,17 @@ static void deformVerts(ModifierData *md,
surmd->cfra = cfra;
- surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh");
+ const bool has_poly = surmd->mesh->totpoly > 0;
+ const bool has_edge = surmd->mesh->totedge > 0;
+ if (has_poly || has_edge) {
+ surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh");
- if (surmd->mesh->totpoly) {
- BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2);
- }
- else {
- BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2);
+ if (has_poly) {
+ BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2);
+ }
+ else if (has_edge) {
+ BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2);
+ }
}
}
}
@@ -241,7 +245,6 @@ ModifierTypeInfo modifierType_Surface = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index 99011c5e351..0cc68c2c4a3 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -1646,7 +1646,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index 04d24ac0883..ef633494c7b 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -178,7 +178,6 @@ ModifierTypeInfo modifierType_Triangulate = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL, // requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c
index 6b2facc16a2..0be5c164089 100644
--- a/source/blender/modifiers/intern/MOD_ui_common.c
+++ b/source/blender/modifiers/intern/MOD_ui_common.c
@@ -344,14 +344,39 @@ static void modifier_panel_header(const bContext *C, Panel *panel)
}
} /* Tessellation point for curve-typed objects. */
else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
- if (mti->type != eModifierTypeType_Constructive) {
+ /* Some modifiers can work with pre-tessellated curves only. */
+ if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) {
+ /* Add button (appearing to be ON) and add tip why this cant be changed. */
+ sub = uiLayoutRow(row, true);
+ uiBlock *block = uiLayoutGetBlock(sub);
+ static int apply_on_spline_always_on_hack = eModifierMode_ApplyOnSpline;
+ uiBut *but = uiDefIconButBitI(block,
+ UI_BTYPE_TOGGLE,
+ eModifierMode_ApplyOnSpline,
+ 0,
+ ICON_SURFACE_DATA,
+ 0,
+ 0,
+ UI_UNIT_X - 2,
+ UI_UNIT_Y,
+ &apply_on_spline_always_on_hack,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Apply on Spline"));
+ UI_but_disable(
+ but, TIP_("This modifier can only deform control points, not the filled curve/surface"));
+ buttons_number++;
+ }
+ else if (mti->type != eModifierTypeType_Constructive) {
/* Constructive modifiers tessellates curve before applying. */
uiItemR(row, ptr, "use_apply_on_spline", 0, "", ICON_NONE);
buttons_number++;
}
}
/* Collision and Surface are always enabled, hide buttons. */
- if ((md->type != eModifierType_Collision) && (md->type != eModifierType_Surface)) {
+ if (!ELEM(md->type, eModifierType_Collision, eModifierType_Surface)) {
if (mti->flags & eModifierTypeFlag_SupportsEditmode) {
sub = uiLayoutRow(row, true);
uiLayoutSetActive(sub, (md->mode & eModifierMode_Realtime));
diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c
index 3162a33edc2..724d1370a47 100644
--- a/source/blender/modifiers/intern/MOD_uvproject.c
+++ b/source/blender/modifiers/intern/MOD_uvproject.c
@@ -385,7 +385,6 @@ ModifierTypeInfo modifierType_UVProject = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index 77b79167c2f..5742144b6dd 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -342,7 +342,6 @@ ModifierTypeInfo modifierType_UVWarp = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc
index 745e089b8ff..af4b31d6bfc 100644
--- a/source/blender/modifiers/intern/MOD_volume_displace.cc
+++ b/source/blender/modifiers/intern/MOD_volume_displace.cc
@@ -18,6 +18,7 @@
* \ingroup modifiers
*/
+#include "BKE_geometry_set.hh"
#include "BKE_lib_query.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
@@ -284,7 +285,7 @@ struct DisplaceGridOp {
#endif
-static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
+static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
{
#ifdef WITH_OPENVDB
VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
@@ -293,7 +294,7 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
BKE_volume_load(volume, DEG_get_bmain(ctx->depsgraph));
const int grid_amount = BKE_volume_num_grids(volume);
for (int grid_index = 0; grid_index < grid_amount; grid_index++) {
- VolumeGrid *volume_grid = BKE_volume_grid_get(volume, grid_index);
+ VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, grid_index);
BLI_assert(volume_grid != nullptr);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
@@ -303,14 +304,22 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
BKE_volume_grid_type_operation(grid_type, displace_grid_op);
}
- return volume;
#else
- UNUSED_VARS(md, ctx);
+ UNUSED_VARS(md, volume, ctx);
BKE_modifier_set_error(ctx->object, md, "Compiled without OpenVDB");
- return volume;
#endif
}
+static void modifyGeometrySet(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
+{
+ Volume *input_volume = geometry_set->get_volume_for_write();
+ if (input_volume != nullptr) {
+ displace_volume(md, ctx, input_volume);
+ }
+}
+
ModifierTypeInfo modifierType_VolumeDisplace = {
/* name */ "Volume Displace",
/* structName */ "VolumeDisplaceModifierData",
@@ -328,8 +337,7 @@ ModifierTypeInfo modifierType_VolumeDisplace = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
- /* modifyGeometrySet */ nullptr,
- /* modifyVolume */ modifyVolume,
+ /* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
index 41ed7ae983a..c0bf07b8eec 100644
--- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
+++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
@@ -155,10 +155,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return create_empty_mesh(input_mesh);
}
- Volume *volume = static_cast<Volume *>(vmmd->object->data);
+ const Volume *volume = static_cast<Volume *>(vmmd->object->data);
BKE_volume_load(volume, DEG_get_bmain(ctx->depsgraph));
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, vmmd->grid_name);
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, vmmd->grid_name);
if (volume_grid == nullptr) {
BKE_modifier_set_error(ctx->object, md, "Cannot find '%s' grid", vmmd->grid_name);
return create_empty_mesh(input_mesh);
@@ -223,7 +223,6 @@ ModifierTypeInfo modifierType_VolumeToMesh = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 9d3d5b0658c..3bebc52c503 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -539,7 +539,6 @@ ModifierTypeInfo modifierType_Warp = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 863656b85a5..c6bab89247e 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -492,7 +492,6 @@ ModifierTypeInfo modifierType_Wave = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index 40265e37db9..2f2da7d6554 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -764,7 +764,6 @@ ModifierTypeInfo modifierType_WeightedNormal = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index 915adccc745..b5f72c88800 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -428,7 +428,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index 52cee7ce7e5..a71a2f3b480 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -514,7 +514,6 @@ ModifierTypeInfo modifierType_WeightVGMix = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index aac29cabf0f..cd03175f16c 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -768,7 +768,6 @@ ModifierTypeInfo modifierType_WeightVGProximity = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c
index fd1254fc948..1590f342666 100644
--- a/source/blender/modifiers/intern/MOD_weld.c
+++ b/source/blender/modifiers/intern/MOD_weld.c
@@ -2053,7 +2053,6 @@ ModifierTypeInfo modifierType_Weld = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c
index 3d8e74d2cf5..16bf1f7d763 100644
--- a/source/blender/modifiers/intern/MOD_wireframe.c
+++ b/source/blender/modifiers/intern/MOD_wireframe.c
@@ -196,7 +196,6 @@ ModifierTypeInfo modifierType_Wireframe = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 42679f39f18..54925ea3317 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -47,6 +47,7 @@ set(INC
set(SRC
composite/nodes/node_composite_alphaOver.c
+ composite/nodes/node_composite_antialiasing.c
composite/nodes/node_composite_bilateralblur.c
composite/nodes/node_composite_blur.c
composite/nodes/node_composite_bokehblur.c
@@ -140,33 +141,48 @@ set(SRC
function/node_function_util.cc
geometry/nodes/node_geo_align_rotation_to_vector.cc
+ geometry/nodes/node_geo_attribute_clamp.cc
geometry/nodes/node_geo_attribute_color_ramp.cc
geometry/nodes/node_geo_attribute_combine_xyz.cc
geometry/nodes/node_geo_attribute_compare.cc
geometry/nodes/node_geo_attribute_convert.cc
+ geometry/nodes/node_geo_attribute_curve_map.cc
geometry/nodes/node_geo_attribute_fill.cc
+ geometry/nodes/node_geo_attribute_map_range.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_attribute_proximity.cc
geometry/nodes/node_geo_attribute_randomize.cc
+ geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_separate_xyz.cc
+ geometry/nodes/node_geo_attribute_transfer.cc
geometry/nodes/node_geo_attribute_vector_math.cc
- geometry/nodes/node_geo_attribute_remove.cc
+ geometry/nodes/node_geo_attribute_vector_rotate.cc
geometry/nodes/node_geo_boolean.cc
+ geometry/nodes/node_geo_bounding_box.cc
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
+ geometry/nodes/node_geo_convex_hull.cc
+ geometry/nodes/node_geo_curve_length.cc
+ geometry/nodes/node_geo_curve_to_mesh.cc
+ geometry/nodes/node_geo_curve_resample.cc
+ geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_edge_split.cc
+ geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
+ geometry/nodes/node_geo_material_assign.cc
+ geometry/nodes/node_geo_material_replace.cc
geometry/nodes/node_geo_mesh_primitive_circle.cc
geometry/nodes/node_geo_mesh_primitive_cone.cc
geometry/nodes/node_geo_mesh_primitive_cube.cc
geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+ geometry/nodes/node_geo_mesh_primitive_grid.cc
geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
geometry/nodes/node_geo_mesh_primitive_line.cc
- geometry/nodes/node_geo_mesh_primitive_plane.cc
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+ geometry/nodes/node_geo_mesh_to_curve.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
@@ -175,8 +191,10 @@ set(SRC
geometry/nodes/node_geo_point_separate.cc
geometry/nodes/node_geo_point_translate.cc
geometry/nodes/node_geo_points_to_volume.cc
+ geometry/nodes/node_geo_select_by_material.cc
geometry/nodes/node_geo_subdivide.cc
geometry/nodes/node_geo_subdivision_surface.cc
+ geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/nodes/node_geo_volume_to_mesh.cc
@@ -207,7 +225,7 @@ set(SRC
shader/nodes/node_shader_camera.c
shader/nodes/node_shader_clamp.cc
shader/nodes/node_shader_common.c
- shader/nodes/node_shader_curves.c
+ shader/nodes/node_shader_curves.cc
shader/nodes/node_shader_displacement.c
shader/nodes/node_shader_eevee_specular.c
shader/nodes/node_shader_emission.c
@@ -225,7 +243,7 @@ set(SRC
shader/nodes/node_shader_map_range.cc
shader/nodes/node_shader_mapping.c
shader/nodes/node_shader_math.cc
- shader/nodes/node_shader_mixRgb.c
+ shader/nodes/node_shader_mixRgb.cc
shader/nodes/node_shader_mix_shader.c
shader/nodes/node_shader_normal.c
shader/nodes/node_shader_normal_map.c
@@ -306,13 +324,14 @@ set(SRC
intern/derived_node_tree.cc
intern/math_functions.cc
intern/node_common.c
- intern/node_exec.c
+ intern/node_exec.cc
intern/node_geometry_exec.cc
intern/node_socket.cc
intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
intern/type_callbacks.cc
+ intern/type_conversions.cc
composite/node_composite_util.h
function/node_function_util.hh
@@ -334,6 +353,7 @@ set(SRC
NOD_static_types.h
NOD_texture.h
NOD_type_callbacks.hh
+ NOD_type_conversions.hh
intern/node_common.h
intern/node_exec.h
intern/node_util.h
@@ -345,6 +365,23 @@ set(LIB
bf_intern_sky
)
+if(WITH_BULLET)
+ list(APPEND INC_SYS
+ ${BULLET_INCLUDE_DIRS}
+ "../../../intern/rigidbody/"
+ )
+ if(NOT WITH_SYSTEM_BULLET)
+ list(APPEND LIB
+ extern_bullet
+ )
+ endif()
+
+ list(APPEND LIB
+ ${BULLET_LIBRARIES}
+ )
+ add_definitions(-DWITH_BULLET)
+endif()
+
if(WITH_PYTHON)
list(APPEND INC
../python
@@ -389,6 +426,18 @@ if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
if(WITH_OPENVDB)
list(APPEND INC_SYS
${OPENVDB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index b0dd0edeec5..258e4c961c9 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -79,6 +79,7 @@ void register_node_type_cmp_inpaint(void);
void register_node_type_cmp_despeckle(void);
void register_node_type_cmp_defocus(void);
void register_node_type_cmp_denoise(void);
+void register_node_type_cmp_antialiasing(void);
void register_node_type_cmp_valtorgb(void);
void register_node_type_cmp_rgbtobw(void);
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index c29de611e18..de9e4c8c812 100644
--- a/source/blender/nodes/NOD_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -59,6 +59,7 @@ class DTreeContext {
const NodeTreeRef *tree_;
/* All the children contexts of this context. */
Map<const NodeRef *, DTreeContext *> children_;
+ DerivedNodeTree *derived_tree_;
friend DerivedNodeTree;
@@ -67,6 +68,7 @@ class DTreeContext {
const DTreeContext *parent_context() const;
const NodeRef *parent_node() const;
const DTreeContext *child_context(const NodeRef &node) const;
+ const DerivedNodeTree &derived_tree() const;
bool is_root() const;
};
@@ -90,6 +92,12 @@ class DNode {
operator bool() const;
uint64_t hash() const;
+
+ DInputSocket input(int index) const;
+ DOutputSocket output(int index) const;
+
+ DInputSocket input_by_identifier(StringRef identifier) const;
+ DOutputSocket output_by_identifier(StringRef identifier) const;
};
/* A (nullable) reference to a socket and the context it is in. It is unique within an entire
@@ -117,6 +125,8 @@ class DSocket {
operator bool() const;
uint64_t hash() const;
+
+ DNode node() const;
};
/* A (nullable) reference to an input socket and the context it is in. */
@@ -132,7 +142,7 @@ class DInputSocket : public DSocket {
DOutputSocket get_corresponding_group_node_output() const;
Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const;
- void foreach_origin_socket(FunctionRef<void(DSocket)> callback) const;
+ void foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const;
};
/* A (nullable) reference to an output socket and the context it is in. */
@@ -148,7 +158,8 @@ class DOutputSocket : public DSocket {
DInputSocket get_corresponding_group_node_input() const;
DInputSocket get_active_corresponding_group_output_socket() const;
- void foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const;
+ void foreach_target_socket(FunctionRef<void(DInputSocket)> target_fn,
+ FunctionRef<void(DSocket)> skipped_fn) const;
};
class DerivedNodeTree {
@@ -165,6 +176,7 @@ class DerivedNodeTree {
Span<const NodeTreeRef *> used_node_tree_refs() const;
bool has_link_cycles() const;
+ bool has_undefined_nodes_or_sockets() const;
void foreach_node(FunctionRef<void(DNode)> callback) const;
std::string to_dot() const;
@@ -214,6 +226,11 @@ inline const DTreeContext *DTreeContext::child_context(const NodeRef &node) cons
return children_.lookup_default(&node, nullptr);
}
+inline const DerivedNodeTree &DTreeContext::derived_tree() const
+{
+ return *derived_tree_;
+}
+
inline bool DTreeContext::is_root() const
{
return parent_context_ == nullptr;
@@ -264,6 +281,26 @@ inline uint64_t DNode::hash() const
return get_default_hash_2(context_, node_ref_);
}
+inline DInputSocket DNode::input(int index) const
+{
+ return {context_, &node_ref_->input(index)};
+}
+
+inline DOutputSocket DNode::output(int index) const
+{
+ return {context_, &node_ref_->output(index)};
+}
+
+inline DInputSocket DNode::input_by_identifier(StringRef identifier) const
+{
+ return {context_, &node_ref_->input_by_identifier(identifier)};
+}
+
+inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
+{
+ return {context_, &node_ref_->output_by_identifier(identifier)};
+}
+
/* --------------------------------------------------------------------
* DSocket inline methods.
*/
@@ -319,6 +356,12 @@ inline uint64_t DSocket::hash() const
return get_default_hash_2(context_, socket_ref_);
}
+inline DNode DSocket::node() const
+{
+ BLI_assert(socket_ref_ != nullptr);
+ return {context_, &socket_ref_->node()};
+}
+
/* --------------------------------------------------------------------
* DInputSocket inline methods.
*/
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 03115852d80..b7e1b0b657c 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -20,38 +20,56 @@
extern "C" {
#endif
+#include "BKE_node.h"
+
extern struct bNodeTreeType *ntreeType_Geometry;
void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
+void register_node_type_geo_custom_group(bNodeType *ntype);
void register_node_type_geo_align_rotation_to_vector(void);
+void register_node_type_geo_attribute_clamp(void);
void register_node_type_geo_attribute_color_ramp(void);
void register_node_type_geo_attribute_combine_xyz(void);
void register_node_type_geo_attribute_compare(void);
void register_node_type_geo_attribute_convert(void);
+void register_node_type_geo_attribute_curve_map(void);
void register_node_type_geo_attribute_fill(void);
+void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_separate_xyz(void);
+void register_node_type_geo_attribute_transfer(void);
void register_node_type_geo_attribute_vector_math(void);
+void register_node_type_geo_attribute_vector_rotate(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_boolean(void);
+void register_node_type_geo_bounding_box(void);
void register_node_type_geo_collection_info(void);
+void register_node_type_geo_convex_hull(void);
+void register_node_type_geo_curve_length(void);
+void register_node_type_geo_curve_to_mesh(void);
+void register_node_type_geo_curve_resample(void);
+void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_edge_split(void);
+void register_node_type_geo_input_material(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
+void register_node_type_geo_material_assign(void);
+void register_node_type_geo_material_replace(void);
void register_node_type_geo_mesh_primitive_circle(void);
void register_node_type_geo_mesh_primitive_cone(void);
void register_node_type_geo_mesh_primitive_cube(void);
-void register_node_type_geo_mesh_primitive_plane(void);
void register_node_type_geo_mesh_primitive_cylinder(void);
+void register_node_type_geo_mesh_primitive_grid(void);
void register_node_type_geo_mesh_primitive_ico_sphere(void);
void register_node_type_geo_mesh_primitive_line(void);
void register_node_type_geo_mesh_primitive_uv_sphere(void);
+void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_point_distribute(void);
void register_node_type_geo_point_instance(void);
@@ -61,8 +79,10 @@ void register_node_type_geo_point_separate(void);
void register_node_type_geo_point_translate(void);
void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_sample_texture(void);
+void register_node_type_geo_select_by_material(void);
void register_node_type_geo_subdivide(void);
void register_node_type_geo_subdivision_surface(void);
+void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_volume_to_mesh(void);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index ba606dacdb0..86c98525814 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -22,7 +22,6 @@
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_node_ui_storage.hh"
-#include "BKE_persistent_data_handle.hh"
#include "DNA_node_types.h"
@@ -33,55 +32,90 @@ struct ModifierData;
namespace blender::nodes {
-using bke::BooleanReadAttribute;
-using bke::BooleanWriteAttribute;
-using bke::Color4fReadAttribute;
-using bke::Color4fWriteAttribute;
-using bke::Float2ReadAttribute;
-using bke::Float2WriteAttribute;
-using bke::Float3ReadAttribute;
-using bke::Float3WriteAttribute;
-using bke::FloatReadAttribute;
-using bke::FloatWriteAttribute;
using bke::geometry_set_realize_instances;
-using bke::Int32ReadAttribute;
-using bke::Int32WriteAttribute;
-using bke::PersistentDataHandleMap;
-using bke::PersistentObjectHandle;
-using bke::ReadAttribute;
-using bke::ReadAttributePtr;
-using bke::WriteAttribute;
-using bke::WriteAttributePtr;
+using bke::OutputAttribute;
+using bke::OutputAttribute_Typed;
+using bke::ReadAttributeLookup;
+using bke::WriteAttributeLookup;
using fn::CPPType;
using fn::GMutablePointer;
+using fn::GMutableSpan;
using fn::GPointer;
+using fn::GSpan;
using fn::GValueMap;
+using fn::GVArray;
+using fn::GVArray_GSpan;
+using fn::GVArray_Span;
+using fn::GVArray_Typed;
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArray_GSpan;
+using fn::GVMutableArray_Typed;
+using fn::GVMutableArrayPtr;
+
+/**
+ * This class exists to separate the memory management details of the geometry nodes evaluator from
+ * the node execution functions and related utilities.
+ */
+class GeoNodeExecParamsProvider {
+ public:
+ DNode dnode;
+ const Object *self_object = nullptr;
+ const ModifierData *modifier = nullptr;
+ Depsgraph *depsgraph = nullptr;
+
+ /**
+ * Returns true when the node is allowed to get/extract the input value. The identifier is
+ * expected to be valid. This may return false if the input value has been consumed already.
+ */
+ virtual bool can_get_input(StringRef identifier) const = 0;
+
+ /**
+ * Returns true when the node is allowed to set the output value. The identifier is expected to
+ * be valid. This may return false if the output value has been set already.
+ */
+ virtual bool can_set_output(StringRef identifier) const = 0;
+
+ /**
+ * Take ownership of an input value. The caller is responsible for destructing the value. It does
+ * not have to be freed, because the memory is managed by the geometry nodes evaluator.
+ */
+ virtual GMutablePointer extract_input(StringRef identifier) = 0;
+
+ /**
+ * Similar to #extract_input, but has to be used for multi-input sockets.
+ */
+ virtual Vector<GMutablePointer> extract_multi_input(StringRef identifier) = 0;
+
+ /**
+ * Get the input value for the identifier without taking ownership of it.
+ */
+ virtual GPointer get_input(StringRef identifier) const = 0;
+
+ /**
+ * Prepare a memory buffer for an output value of the node. The returned memory has to be
+ * initialized by the caller. The identifier and type are expected to be correct.
+ */
+ virtual GMutablePointer alloc_output_value(const CPPType &type) = 0;
+
+ /**
+ * The value has been allocated with #alloc_output_value.
+ */
+ virtual void set_output(StringRef identifier, GMutablePointer value) = 0;
+
+ /* A description for these methods is provided in GeoNodeExecParams. */
+ virtual void set_input_unused(StringRef identifier) = 0;
+ virtual bool output_is_required(StringRef identifier) const = 0;
+ virtual bool lazy_require_input(StringRef identifier) = 0;
+ virtual bool lazy_output_is_required(StringRef identifier) const = 0;
+};
class GeoNodeExecParams {
private:
- const DNode node_;
- GValueMap<StringRef> &input_values_;
- GValueMap<StringRef> &output_values_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
+ GeoNodeExecParamsProvider *provider_;
public:
- GeoNodeExecParams(const DNode node,
- GValueMap<StringRef> &input_values,
- GValueMap<StringRef> &output_values,
- const PersistentDataHandleMap &handle_map,
- const Object *self_object,
- const ModifierData *modifier,
- Depsgraph *depsgraph)
- : node_(node),
- input_values_(input_values),
- output_values_(output_values),
- handle_map_(handle_map),
- self_object_(self_object),
- modifier_(modifier),
- depsgraph_(depsgraph)
+ GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider)
{
}
@@ -94,9 +128,9 @@ class GeoNodeExecParams {
GMutablePointer extract_input(StringRef identifier)
{
#ifdef DEBUG
- this->check_extract_input(identifier);
+ this->check_input_access(identifier);
#endif
- return input_values_.extract(identifier);
+ return provider_->extract_input(identifier);
}
/**
@@ -107,9 +141,10 @@ class GeoNodeExecParams {
template<typename T> T extract_input(StringRef identifier)
{
#ifdef DEBUG
- this->check_extract_input(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- return input_values_.extract<T>(identifier);
+ GMutablePointer gvalue = this->extract_input(identifier);
+ return gvalue.relocate_out<T>();
}
/**
@@ -119,18 +154,10 @@ class GeoNodeExecParams {
*/
template<typename T> Vector<T> extract_multi_input(StringRef identifier)
{
+ Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier);
Vector<T> values;
- int index = 0;
- while (true) {
- std::string sub_identifier = identifier;
- if (index > 0) {
- sub_identifier += "[" + std::to_string(index) + "]";
- }
- if (!input_values_.contains(sub_identifier)) {
- break;
- }
- values.append(input_values_.extract<T, StringRef>(sub_identifier));
- index++;
+ for (GMutablePointer gvalue : gvalues) {
+ values.append(gvalue.relocate_out<T>());
}
return values;
}
@@ -141,67 +168,83 @@ class GeoNodeExecParams {
template<typename T> const T &get_input(StringRef identifier) const
{
#ifdef DEBUG
- this->check_extract_input(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- return input_values_.lookup<T>(identifier);
+ GPointer gvalue = provider_->get_input(identifier);
+ BLI_assert(gvalue.is_type<T>());
+ return *(const T *)gvalue.get();
}
/**
- * Move-construct a new value based on the given value and store it for the given socket
- * identifier.
+ * Store the output value for the given socket identifier.
*/
- void set_output_by_move(StringRef identifier, GMutablePointer value)
+ template<typename T> void set_output(StringRef identifier, T &&value)
{
+ using StoredT = std::decay_t<T>;
+ const CPPType &type = CPPType::get<std::decay_t<T>>();
#ifdef DEBUG
- BLI_assert(value.type() != nullptr);
- BLI_assert(value.get() != nullptr);
- this->check_set_output(identifier, *value.type());
+ this->check_output_access(identifier, type);
#endif
- output_values_.add_new_by_move(identifier, value);
+ GMutablePointer gvalue = provider_->alloc_output_value(type);
+ new (gvalue.get()) StoredT(std::forward<T>(value));
+ provider_->set_output(identifier, gvalue);
}
- void set_output_by_copy(StringRef identifier, GPointer value)
+ /**
+ * Tell the evaluator that a specific input won't be used anymore.
+ */
+ void set_input_unused(StringRef identifier)
{
-#ifdef DEBUG
- BLI_assert(value.type() != nullptr);
- BLI_assert(value.get() != nullptr);
- this->check_set_output(identifier, *value.type());
-#endif
- output_values_.add_new_by_copy(identifier, value);
+ provider_->set_input_unused(identifier);
}
/**
- * Store the output value for the given socket identifier.
+ * Returns true when the output has to be computed.
+ * Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid
+ * some computations.
*/
- template<typename T> void set_output(StringRef identifier, T &&value)
+ bool output_is_required(StringRef identifier) const
{
-#ifdef DEBUG
- this->check_set_output(identifier, CPPType::get<std::decay_t<T>>());
-#endif
- output_values_.add_new(identifier, std::forward<T>(value));
+ return provider_->output_is_required(identifier);
}
/**
- * Get the node that is currently being executed.
+ * Tell the evaluator that a specific input is required.
+ * This returns true when the input will only be available in the next execution.
+ * False is returned if the input is available already.
+ * This can only be used when the node supports laziness.
*/
- const bNode &node() const
+ bool lazy_require_input(StringRef identifier)
{
- return *node_->bnode();
+ return provider_->lazy_require_input(identifier);
}
- const PersistentDataHandleMap &handle_map() const
+ /**
+ * Asks the evaluator if a specific output is required right now. If this returns false, the
+ * value might still need to be computed later.
+ * This can only be used when the node supports laziness.
+ */
+ bool lazy_output_is_required(StringRef identifier)
+ {
+ return provider_->lazy_output_is_required(identifier);
+ }
+
+ /**
+ * Get the node that is currently being executed.
+ */
+ const bNode &node() const
{
- return handle_map_;
+ return *provider_->dnode->bnode();
}
const Object *self_object() const
{
- return self_object_;
+ return provider_->self_object;
}
Depsgraph *depsgraph() const
{
- return depsgraph_;
+ return provider_->depsgraph;
}
/**
@@ -217,20 +260,21 @@ class GeoNodeExecParams {
* \note This will add an error message if the string socket is active and
* the input attribute does not exist.
*/
- ReadAttributePtr get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const;
+ GVArrayPtr get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const CustomDataType type,
+ const void *default_value) const;
template<typename T>
- bke::TypedReadAttribute<T> get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const T &default_value) const
+ GVArray_Typed<T> get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const T &default_value) const
{
const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>());
- return this->get_input_attribute(name, component, domain, type, &default_value);
+ GVArrayPtr varray = this->get_input_attribute(name, component, domain, type, &default_value);
+ return GVArray_Typed<T>(std::move(varray));
}
/**
@@ -247,8 +291,8 @@ class GeoNodeExecParams {
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;
+ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
+ void check_output_access(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;
diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh
index 08496e044c5..f3eb1c24087 100644
--- a/source/blender/nodes/NOD_math_functions.hh
+++ b/source/blender/nodes/NOD_math_functions.hh
@@ -347,6 +347,8 @@ inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOpera
};
switch (operation) {
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ return dispatch([](float3 a, float3 b, float3 c) { return a * b + c; });
case NODE_VECTOR_MATH_WRAP:
return dispatch([](float3 a, float3 b, float3 c) {
return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z));
@@ -379,7 +381,8 @@ inline bool try_dispatch_float_math_fl3_fl3_fl_to_fl3(const NodeVectorMathOperat
switch (operation) {
case NODE_VECTOR_MATH_REFRACT:
- return dispatch([](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); });
+ return dispatch(
+ [](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); });
default:
return false;
}
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
index df31ee18369..e3f31e011e4 100644
--- a/source/blender/nodes/NOD_node_tree_multi_function.hh
+++ b/source/blender/nodes/NOD_node_tree_multi_function.hh
@@ -29,7 +29,7 @@
#include "NOD_type_callbacks.hh"
#include "BLI_multi_value_map.hh"
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
namespace blender::nodes {
@@ -190,7 +190,7 @@ class MFNetworkTreeMap {
* This data is necessary throughout the generation of a MFNetwork from a node tree.
*/
struct CommonMFNetworkBuilderData {
- ResourceCollector &resources;
+ ResourceScope &scope;
fn::MFNetwork &network;
MFNetworkTreeMap &network_map;
const DerivedNodeTree &tree;
@@ -225,9 +225,9 @@ class MFNetworkBuilderBase {
* Returns a resource collector that will only be destructed after the multi-function network is
* destructed.
*/
- ResourceCollector &resources()
+ ResourceScope &resource_scope()
{
- return common_.resources;
+ return common_.scope;
}
/**
@@ -236,9 +236,9 @@ class MFNetworkBuilderBase {
template<typename T, typename... Args> T &construct_fn(Args &&... args)
{
BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
- void *buffer = common_.resources.linear_allocator().allocate(sizeof(T), alignof(T));
+ void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T));
T *fn = new (buffer) T(std::forward<Args>(args)...);
- common_.resources.add(destruct_ptr<T>(fn), fn->name().c_str());
+ common_.scope.add(destruct_ptr<T>(fn), fn->name().c_str());
return *fn;
}
};
@@ -382,39 +382,9 @@ class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
- ResourceCollector &resources);
+ ResourceScope &scope);
using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>;
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
- ResourceCollector &resources);
-
-class DataTypeConversions {
- private:
- Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *> conversions_;
-
- public:
- void add(fn::MFDataType from_type, fn::MFDataType to_type, const fn::MultiFunction &fn)
- {
- conversions_.add_new({from_type, to_type}, &fn);
- }
-
- const fn::MultiFunction *get_conversion(fn::MFDataType from, fn::MFDataType to) const
- {
- return conversions_.lookup_default({from, to}, nullptr);
- }
-
- bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
- {
- return conversions_.contains(
- {fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
- }
-
- void convert(const CPPType &from_type,
- const CPPType &to_type,
- const void *from_value,
- void *to_value) const;
-};
-
-const DataTypeConversions &get_implicit_type_conversions();
+MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope);
} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
index 3710bd2fe00..b028fc28bbc 100644
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -70,6 +70,8 @@ class NodeTreeRef;
class LinkRef;
class InternalLinkRef;
+using SocketIndexByIdentifierMap = Map<std::string, int>;
+
class SocketRef : NonCopyable, NonMovable {
protected:
NodeRef *node_;
@@ -81,15 +83,19 @@ class SocketRef : NonCopyable, NonMovable {
Vector<LinkRef *> directly_linked_links_;
/* These sockets are linked directly, i.e. with a single link in between. */
- MutableSpan<SocketRef *> directly_linked_sockets_;
+ MutableSpan<const SocketRef *> directly_linked_sockets_;
/* These sockets are linked when reroutes, muted links and muted nodes have been taken into
* account. */
- MutableSpan<SocketRef *> logically_linked_sockets_;
+ MutableSpan<const SocketRef *> logically_linked_sockets_;
+ /* These are the sockets that have been skipped when searching for logically linked sockets.
+ * That includes for example the input and output socket of an intermediate reroute node. */
+ MutableSpan<const SocketRef *> logically_linked_skipped_sockets_;
friend NodeTreeRef;
public:
Span<const SocketRef *> logically_linked_sockets() const;
+ Span<const SocketRef *> logically_linked_skipped_sockets() const;
Span<const SocketRef *> directly_linked_sockets() const;
Span<const LinkRef *> directly_linked_links() const;
@@ -121,6 +127,7 @@ class SocketRef : NonCopyable, NonMovable {
bNodeTree *btree() const;
bool is_available() const;
+ bool is_undefined() const;
void *default_value() const;
template<typename T> T *default_value() const;
@@ -128,16 +135,31 @@ class SocketRef : NonCopyable, NonMovable {
class InputSocketRef final : public SocketRef {
public:
+ friend NodeTreeRef;
+
Span<const OutputSocketRef *> logically_linked_sockets() const;
Span<const OutputSocketRef *> directly_linked_sockets() const;
bool is_multi_input_socket() const;
+
+ private:
+ void foreach_logical_origin(FunctionRef<void(const OutputSocketRef &)> origin_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ bool only_follow_first_input_link,
+ Vector<const InputSocketRef *> &handled_sockets) const;
};
class OutputSocketRef final : public SocketRef {
public:
+ friend NodeTreeRef;
+
Span<const InputSocketRef *> logically_linked_sockets() const;
Span<const InputSocketRef *> directly_linked_sockets() const;
+
+ private:
+ void foreach_logical_target(FunctionRef<void(const InputSocketRef &)> target_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ Vector<const OutputSocketRef *> &handled_sockets) const;
};
class NodeRef : NonCopyable, NonMovable {
@@ -149,6 +171,8 @@ class NodeRef : NonCopyable, NonMovable {
Vector<InputSocketRef *> inputs_;
Vector<OutputSocketRef *> outputs_;
Vector<InternalLinkRef *> internal_links_;
+ SocketIndexByIdentifierMap *input_index_by_identifier_;
+ SocketIndexByIdentifierMap *output_index_by_identifier_;
friend NodeTreeRef;
@@ -162,6 +186,9 @@ class NodeRef : NonCopyable, NonMovable {
const InputSocketRef &input(int index) const;
const OutputSocketRef &output(int index) const;
+ const InputSocketRef &input_by_identifier(StringRef identifier) const;
+ const OutputSocketRef &output_by_identifier(StringRef identifier) const;
+
bNode *bnode() const;
bNodeTree *btree() const;
@@ -178,6 +205,7 @@ class NodeRef : NonCopyable, NonMovable {
bool is_group_output_node() const;
bool is_muted() const;
bool is_frame() const;
+ bool is_undefined() const;
void *storage() const;
template<typename T> T *storage() const;
@@ -225,6 +253,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
Vector<OutputSocketRef *> output_sockets_;
Vector<LinkRef *> links_;
MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_;
+ Vector<std::unique_ptr<SocketIndexByIdentifierMap>> owned_identifier_maps_;
public:
NodeTreeRef(bNodeTree *btree);
@@ -241,6 +270,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
Span<const LinkRef *> links() const;
bool has_link_cycles() const;
+ bool has_undefined_nodes_or_sockets() const;
bNodeTree *btree() const;
StringRefNull name() const;
@@ -257,12 +287,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
bNodeSocket *bsocket);
void create_linked_socket_caches();
-
- void foreach_logical_origin(InputSocketRef &socket,
- FunctionRef<void(OutputSocketRef &)> callback,
- bool only_follow_first_input_link = false);
- void foreach_logical_target(OutputSocketRef &socket,
- FunctionRef<void(InputSocketRef &)> callback);
+ void create_socket_identifier_maps();
};
using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>;
@@ -287,6 +312,11 @@ inline Span<const SocketRef *> SocketRef::logically_linked_sockets() const
return logically_linked_sockets_;
}
+inline Span<const SocketRef *> SocketRef::logically_linked_skipped_sockets() const
+{
+ return logically_linked_skipped_sockets_;
+}
+
inline Span<const SocketRef *> SocketRef::directly_linked_sockets() const
{
return directly_linked_sockets_;
@@ -399,6 +429,11 @@ inline bool SocketRef::is_available() const
return (bsocket_->flag & SOCK_UNAVAIL) == 0;
}
+inline bool SocketRef::is_undefined() const
+{
+ return bsocket_->typeinfo == &NodeSocketTypeUndefined;
+}
+
inline void *SocketRef::default_value() const
{
return bsocket_->default_value;
@@ -476,6 +511,18 @@ inline const OutputSocketRef &NodeRef::output(int index) const
return *outputs_[index];
}
+inline const InputSocketRef &NodeRef::input_by_identifier(StringRef identifier) const
+{
+ const int index = input_index_by_identifier_->lookup_as(identifier);
+ return this->input(index);
+}
+
+inline const OutputSocketRef &NodeRef::output_by_identifier(StringRef identifier) const
+{
+ const int index = output_index_by_identifier_->lookup_as(identifier);
+ return this->output(index);
+}
+
inline bNode *NodeRef::bnode() const
{
return bnode_;
@@ -518,7 +565,7 @@ inline bool NodeRef::is_reroute_node() const
inline bool NodeRef::is_group_node() const
{
- return bnode_->type == NODE_GROUP;
+ return bnode_->type == NODE_GROUP || bnode_->type == NODE_CUSTOM_GROUP;
}
inline bool NodeRef::is_group_input_node() const
@@ -536,6 +583,11 @@ inline bool NodeRef::is_frame() const
return bnode_->type == NODE_FRAME;
}
+inline bool NodeRef::is_undefined() const
+{
+ return bnode_->typeinfo == &NodeTypeUndefined;
+}
+
inline bool NodeRef::is_muted() const
{
return (bnode_->flag & NODE_MUTED) != 0;
diff --git a/source/blender/nodes/NOD_socket.h b/source/blender/nodes/NOD_socket.h
index 3344a25bdea..6bcfda70d04 100644
--- a/source/blender/nodes/NOD_socket.h
+++ b/source/blender/nodes/NOD_socket.h
@@ -41,7 +41,7 @@ extern "C" {
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
- int in_out);
+ eNodeSocketInOut in_out);
void node_verify_socket_templates(struct bNodeTree *ntree, struct bNode *node);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index ec687ae6b70..b255c6e5f23 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -224,6 +224,7 @@ DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE, def_cmp_cryptomatte, "CRYPTO
DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE_LEGACY, def_cmp_cryptomatte_legacy, "CRYPTOMATTE", Cryptomatte, "Cryptomatte (Legacy)", "" )
DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOISE", Denoise, "Denoise", "" )
DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" )
+DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" )
DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )
@@ -262,50 +263,67 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "")
-DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
+DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
+DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
-DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
-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, 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, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
-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_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "")
-DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "")
-DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
-DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
-DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
-DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "")
+DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
+DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
+DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
+DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
+DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
+DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_PLANE, 0, "MESH_PRIMITIVE_PLANE", MeshPlane, "Plane", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
+DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
+DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
+DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
+DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "")
+DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
+DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
+DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
+DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
+DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
+DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
+DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/NOD_type_conversions.hh b/source/blender/nodes/NOD_type_conversions.hh
new file mode 100644
index 00000000000..ec4859f0657
--- /dev/null
+++ b/source/blender/nodes/NOD_type_conversions.hh
@@ -0,0 +1,83 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "FN_multi_function.hh"
+
+namespace blender::nodes {
+
+using fn::CPPType;
+using fn::GVArray;
+
+struct ConversionFunctions {
+ const fn::MultiFunction *multi_function;
+ void (*convert_single_to_initialized)(const void *src, void *dst);
+ void (*convert_single_to_uninitialized)(const void *src, void *dst);
+};
+
+class DataTypeConversions {
+ private:
+ Map<std::pair<fn::MFDataType, fn::MFDataType>, ConversionFunctions> conversions_;
+
+ public:
+ void add(fn::MFDataType from_type,
+ fn::MFDataType to_type,
+ const fn::MultiFunction &fn,
+ void (*convert_single_to_initialized)(const void *src, void *dst),
+ void (*convert_single_to_uninitialized)(const void *src, void *dst))
+ {
+ conversions_.add_new({from_type, to_type},
+ {&fn, convert_single_to_initialized, convert_single_to_uninitialized});
+ }
+
+ const ConversionFunctions *get_conversion_functions(fn::MFDataType from, fn::MFDataType to) const
+ {
+ return conversions_.lookup_ptr({from, to});
+ }
+
+ const ConversionFunctions *get_conversion_functions(const CPPType &from, const CPPType &to) const
+ {
+ return this->get_conversion_functions(fn::MFDataType::ForSingle(from),
+ fn::MFDataType::ForSingle(to));
+ }
+
+ const fn::MultiFunction *get_conversion_multi_function(fn::MFDataType from,
+ fn::MFDataType to) const
+ {
+ const ConversionFunctions *functions = this->get_conversion_functions(from, to);
+ return functions ? functions->multi_function : nullptr;
+ }
+
+ bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
+ {
+ return conversions_.contains(
+ {fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
+ }
+
+ void convert_to_uninitialized(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value) const;
+
+ fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const;
+
+ fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const;
+};
+
+const DataTypeConversions &get_implicit_type_conversions();
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index 085b5c463b9..19815d01278 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -205,6 +205,12 @@ static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode)
}
}
+static bool composite_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
+}
+
bNodeTreeType *ntreeType_Composite;
void register_node_tree_type_cmp(void)
@@ -227,6 +233,7 @@ void register_node_tree_type_cmp(void)
tt->update = update;
tt->get_from_context = composite_get_from_context;
tt->node_add_init = composite_node_add_init;
+ tt->valid_socket_type = composite_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_CompositorNodeTree;
diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.c
index b6cbffea413..6cc17d8c272 100644
--- a/source/blender/nodes/composite/node_composite_util.c
+++ b/source/blender/nodes/composite/node_composite_util.c
@@ -23,9 +23,15 @@
#include "node_composite_util.h"
-bool cmp_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool cmp_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "CompositorNodeTree");
+ if (!STREQ(ntree->idname, "CompositorNodeTree")) {
+ *r_disabled_hint = "Not a compositor node tree";
+ return false;
+ }
+ return true;
}
void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node)
diff --git a/source/blender/nodes/composite/node_composite_util.h b/source/blender/nodes/composite/node_composite_util.h
index 800c55df4d6..4fcccbb79f0 100644
--- a/source/blender/nodes/composite/node_composite_util.h
+++ b/source/blender/nodes/composite/node_composite_util.h
@@ -54,7 +54,9 @@ extern "C" {
#define CMP_SCALE_MAX 12000
-bool cmp_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool cmp_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_info);
void cmp_node_update_default(struct bNodeTree *ntree, struct bNode *node);
void cmp_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
new file mode 100644
index 00000000000..7437496d878
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
@@ -0,0 +1,65 @@
+/*
+ * ***** 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.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): IRIE Shinsuke
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/composite/nodes/node_composite_antialiasing.c
+ * \ingroup cmpnodes
+ */
+
+#include "node_composite_util.h"
+
+/* **************** Anti-Aliasing (SMAA 1x) ******************** */
+
+static bNodeSocketTemplate cmp_node_antialiasing_in[] = {
+ {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}};
+
+static bNodeSocketTemplate cmp_node_antialiasing_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}};
+
+static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data");
+
+ data->threshold = 1.0f;
+ data->contrast_limit = 0.2f;
+ data->corner_rounding = 0.25f;
+
+ node->storage = data;
+}
+
+void register_node_type_cmp_antialiasing(void)
+{
+ static bNodeType ntype;
+
+ cmp_node_type_base(
+ &ntype, CMP_NODE_ANTIALIASING, "Anti-Aliasing", NODE_CLASS_OP_FILTER, NODE_PREVIEW);
+ node_type_socket_templates(&ntype, cmp_node_antialiasing_in, cmp_node_antialiasing_out);
+ node_type_size(&ntype, 170, 140, 200);
+ node_type_init(&ntype, node_composit_init_antialiasing);
+ node_type_storage(
+ &ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index c5135482ec2..51dd73a86af 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -40,61 +40,74 @@
/** \name Cryptomatte
* \{ */
+static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render(
+ const bNode &node, const bool use_meta_data)
+{
+ blender::bke::cryptomatte::CryptomatteSessionPtr session;
+
+ Scene *scene = (Scene *)node.id;
+ if (!scene) {
+ return session;
+ }
+ BLI_assert(GS(scene->id.name) == ID_SCE);
+
+ if (use_meta_data) {
+ Render *render = RE_GetSceneRender(scene);
+ RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
+ if (render_result) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_render_result(render_result));
+ }
+ if (render) {
+ RE_ReleaseResult(render);
+ }
+ }
+
+ if (session == nullptr) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_scene(scene));
+ }
+ return session;
+}
+
+static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_image(
+ const Scene &scene, const bNode &node)
+{
+ blender::bke::cryptomatte::CryptomatteSessionPtr session;
+ Image *image = (Image *)node.id;
+ if (!image) {
+ return session;
+ }
+ BLI_assert(GS(image->id.name) == ID_IM);
+
+ NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage);
+ ImageUser *iuser = &node_cryptomatte->iuser;
+ BKE_image_user_frame_calc(image, iuser, scene.r.cfra);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr);
+ RenderResult *render_result = image->rr;
+ if (render_result) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_render_result(render_result));
+ }
+ BKE_image_release_ibuf(image, ibuf, nullptr);
+ return session;
+}
static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node(
- const bNode &node, const int frame_number, const bool use_meta_data)
+ const Scene &scene, const bNode &node, const bool use_meta_data)
{
blender::bke::cryptomatte::CryptomatteSessionPtr session;
if (node.type != CMP_NODE_CRYPTOMATTE) {
return session;
}
- NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage);
switch (node.custom1) {
case CMP_CRYPTOMATTE_SRC_RENDER: {
- Scene *scene = (Scene *)node.id;
- if (!scene) {
- return session;
- }
- BLI_assert(GS(scene->id.name) == ID_SCE);
-
- if (use_meta_data) {
- Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr;
- RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
- if (render_result) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_render_result(render_result));
- }
- if (render) {
- RE_ReleaseResult(render);
- }
- }
-
- if (session == nullptr) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_scene(scene));
- }
-
- break;
+ return cryptomatte_init_from_node_render(node, use_meta_data);
}
case CMP_CRYPTOMATTE_SRC_IMAGE: {
- Image *image = (Image *)node.id;
- if (!image) {
- break;
- }
- BLI_assert(GS(image->id.name) == ID_IM);
-
- ImageUser *iuser = &node_cryptomatte->iuser;
- BKE_image_user_frame_calc(image, iuser, frame_number);
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr);
- RenderResult *render_result = image->rr;
- if (render_result) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_render_result(render_result));
- }
- BKE_image_release_ibuf(image, ibuf, nullptr);
- break;
+ return cryptomatte_init_from_node_image(scene, node);
}
}
return session;
@@ -111,7 +124,10 @@ static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encode
return nullptr;
}
-static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, float encoded_hash)
+static void cryptomatte_add(const Scene &scene,
+ bNode &node,
+ NodeCryptomatte &node_cryptomatte,
+ float encoded_hash)
{
/* Check if entry already exist. */
if (cryptomatte_find(node_cryptomatte, encoded_hash)) {
@@ -121,9 +137,8 @@ static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, floa
CryptomatteEntry *entry = static_cast<CryptomatteEntry *>(
MEM_callocN(sizeof(CryptomatteEntry), __func__));
entry->encoded_hash = encoded_hash;
- /* TODO(jbakker): Get current frame from scene. */
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- node, 0, true);
+ scene, node, true);
if (session) {
BKE_cryptomatte_find_name(session.get(), encoded_hash, entry->name, sizeof(entry->name));
}
@@ -151,12 +166,12 @@ static bNodeSocketTemplate cmp_node_cryptomatte_out[] = {
{-1, ""},
};
-void ntreeCompositCryptomatteSyncFromAdd(bNode *node)
+void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node)
{
BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY));
NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage);
if (n->runtime.add[0] != 0.0f) {
- cryptomatte_add(*node, *n, n->runtime.add[0]);
+ cryptomatte_add(*scene, *node, *n, n->runtime.add[0]);
zero_v3(n->runtime.add);
}
}
@@ -170,14 +185,14 @@ void ntreeCompositCryptomatteSyncFromRemove(bNode *node)
zero_v3(n->runtime.remove);
}
}
-void ntreeCompositCryptomatteUpdateLayerNames(bNode *node)
+void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node)
{
BLI_assert(node->type == CMP_NODE_CRYPTOMATTE);
NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage);
BLI_freelistN(&n->runtime.layers);
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- *node, 0, false);
+ *scene, *node, false);
if (session) {
for (blender::StringRef layer_name :
@@ -190,12 +205,15 @@ void ntreeCompositCryptomatteUpdateLayerNames(bNode *node)
}
}
-void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len)
+void ntreeCompositCryptomatteLayerPrefix(const Scene *scene,
+ const bNode *node,
+ char *r_prefix,
+ size_t prefix_len)
{
BLI_assert(node->type == CMP_NODE_CRYPTOMATTE);
NodeCryptomatte *node_cryptomatte = (NodeCryptomatte *)node->storage;
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- *node, 0, false);
+ *scene, *node, false);
std::string first_layer_name;
if (session) {
@@ -216,10 +234,10 @@ void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size
BLI_strncpy(r_prefix, cstr, prefix_len);
}
-CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node)
+CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node)
{
blender::bke::cryptomatte::CryptomatteSessionPtr session_ptr = cryptomatte_init_from_node(
- *node, 0, true);
+ *scene, *node, true);
return session_ptr.release();
}
@@ -263,7 +281,9 @@ static void node_copy_cryptomatte(bNodeTree *UNUSED(dest_ntree),
dest_node->storage = dest_nc;
}
-static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
if (STREQ(ntree->idname, "CompositorNodeTree")) {
Scene *scene;
@@ -276,8 +296,13 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), bNodeTree *ntree)
}
}
+ if (scene == nullptr) {
+ *r_disabled_hint =
+ "The node tree must be the compositing node tree of any scene in the file";
+ }
return scene != nullptr;
}
+ *r_disabled_hint = "Not a compositor node tree";
return false;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c
index 53ea02ff8a7..243300b0a44 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.c
+++ b/source/blender/nodes/composite/nodes/node_composite_image.c
@@ -526,24 +526,32 @@ static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
}
}
-static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- if (STREQ(ntree->idname, "CompositorNodeTree")) {
- Scene *scene;
+ if (!STREQ(ntree->idname, "CompositorNodeTree")) {
+ *r_disabled_hint = "Not a compositor node tree";
+ return false;
+ }
- /* XXX ugly: check if ntree is a local scene node tree.
- * Render layers node can only be used in local scene->nodetree,
- * since it directly links to the scene.
- */
- for (scene = G.main->scenes.first; scene; scene = scene->id.next) {
- if (scene->nodetree == ntree) {
- break;
- }
+ Scene *scene;
+
+ /* XXX ugly: check if ntree is a local scene node tree.
+ * Render layers node can only be used in local scene->nodetree,
+ * since it directly links to the scene.
+ */
+ for (scene = G.main->scenes.first; scene; scene = scene->id.next) {
+ if (scene->nodetree == ntree) {
+ break;
}
+ }
- return (scene != NULL);
+ if (scene == NULL) {
+ *r_disabled_hint = "The node tree must be the compositing node tree of any scene in the file";
+ return false;
}
- return false;
+ return true;
}
static void node_composit_free_rlayers(bNode *node)
diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc
index 827572d1069..8ff8b416310 100644
--- a/source/blender/nodes/function/node_function_util.cc
+++ b/source/blender/nodes/function/node_function_util.cc
@@ -17,10 +17,16 @@
#include "node_function_util.hh"
#include "node_util.h"
-bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool fn_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
/* Function nodes are only supported in simulation node trees so far. */
- return STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a geometry node tree";
+ return false;
+ }
+ return true;
}
void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index d57d1383019..9fbd6712827 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -38,4 +38,3 @@
void fn_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
-bool fn_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 2c4c7da64cc..f4cd00b88ed 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -67,6 +67,8 @@ static void geometry_node_tree_get_from_context(const bContext *C,
static void geometry_node_tree_update(bNodeTree *ntree)
{
+ ntreeSetOutput(ntree);
+
/* Needed to give correct types to reroutes. */
ntree_update_reroute_nodes(ntree);
}
@@ -82,6 +84,34 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
}
+static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
+{
+ /* Geometry, string, object, material, texture and collection sockets can only be connected to
+ * themselves. The other types can be converted between each other. */
+ if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) &&
+ ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) {
+ return true;
+ }
+ return (link->tosock->type == link->fromsock->type);
+}
+
+static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type,
+ SOCK_FLOAT,
+ SOCK_VECTOR,
+ SOCK_RGBA,
+ SOCK_BOOLEAN,
+ SOCK_INT,
+ SOCK_STRING,
+ SOCK_OBJECT,
+ SOCK_GEOMETRY,
+ SOCK_COLLECTION,
+ SOCK_TEXTURE,
+ SOCK_MATERIAL);
+}
+
void register_node_tree_type_geo(void)
{
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
@@ -95,6 +125,8 @@ void register_node_tree_type_geo(void)
tt->update = geometry_node_tree_update;
tt->get_from_context = geometry_node_tree_get_from_context;
tt->foreach_nodeclass = foreach_nodeclass;
+ tt->valid_socket_type = geometry_node_tree_socket_type_valid;
+ tt->validate_link = geometry_node_tree_validate_link;
ntreeTypeAdd(tt);
}
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index 0f725ecf211..93cada2982b 100644
--- a/source/blender/nodes/geometry/node_geometry_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -56,9 +56,15 @@ void update_attribute_input_socket_availabilities(bNode &node,
} // namespace blender::nodes
-bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool geo_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a geometry node tree";
+ return false;
+ }
+ return true;
}
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index fb80bd08797..79a98c5ebf0 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -36,7 +36,9 @@
void geo_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
-bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool geo_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
namespace blender::nodes {
void update_attribute_input_socket_availabilities(bNode &node,
@@ -58,4 +60,14 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
const int verts_num,
const GeometryNodeMeshCircleFillType fill_type);
+Mesh *create_cube_mesh(const float size);
+
+/**
+ * Copies the point domain attributes from `in_component` that are in the mask to `out_component`.
+ */
+void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ Span<bool> masks,
+ const bool invert);
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
index fca460e3566..d1b71d6f2ba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
@@ -14,13 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_rotation.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_align_rotation_to_vector_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Factor")},
@@ -50,91 +51,117 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
namespace blender::nodes {
-static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
+ MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
+
+ node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
+ node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
+ node->storage;
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ update_attribute_input_socket_availabilities(
+ *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+}
+
+static void align_rotations_auto_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- const float3 new_axis = vector.normalized();
- float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
- if (is_zero_v3(rotation_axis)) {
- /* The vectors are linearly dependent, so we fall back to another axis. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ const float3 new_axis = vector.normalized();
+ float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
if (is_zero_v3(rotation_axis)) {
- /* This is now guaranteed to not be zero. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ /* The vectors are linearly dependent, so we fall back to another axis. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ if (is_zero_v3(rotation_axis)) {
+ /* This is now guaranteed to not be zero. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ }
}
- }
- const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
- const float angle = factors[i] * full_angle;
+ const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, rotation_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, rotation_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
-static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
const float3 local_pivot_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
if (local_main_axis == local_pivot_axis) {
/* Can't compute any meaningful rotation angle in this case. */
return;
}
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- float3 pivot_axis;
- mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
-
- float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
- if (full_angle > M_PI) {
- /* Make sure the point is rotated as little as possible. */
- full_angle -= 2.0f * M_PI;
- }
- const float angle = factors[i] * full_angle;
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float3 pivot_axis;
+ mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
+
+ float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
+ if (full_angle > M_PI) {
+ /* Make sure the point is rotated as little as possible. */
+ full_angle -= 2.0f * M_PI;
+ }
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, pivot_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, pivot_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
static void align_rotations_on_component(GeometryComponent &component,
@@ -144,30 +171,30 @@ static void align_rotations_on_component(GeometryComponent &component,
const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- if (!rotation_attribute) {
+ OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>(
+ "rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
+ if (!rotations) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
- FloatReadAttribute factors = params.get_input_attribute<float>(
+ GVArray_Typed<float> factors = params.get_input_attribute<float>(
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
- Float3ReadAttribute vectors = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 local_main_axis{0, 0, 0};
local_main_axis[storage.axis] = 1;
if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
- align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations);
+ align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span());
}
else {
float3 local_pivot_axis{0, 0, 0};
local_pivot_axis[storage.pivot_axis - 1] = 1;
- align_rotations_fixed_pivot(vectors, factors, local_main_axis, local_pivot_axis, rotations);
+ align_rotations_fixed_pivot(
+ vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span());
}
- rotation_attribute.apply_span_and_save();
+ rotations.save();
}
static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
@@ -183,32 +210,13 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
-
- node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
- node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- node->storage;
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
- update_attribute_input_socket_availabilities(
- *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
-}
-
} // namespace blender::nodes
void register_node_type_geo_align_rotation_to_vector()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
new file mode 100644
index 00000000000..71643df1cb6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
@@ -0,0 +1,284 @@
+
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_clamp_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -100000, 100000},
+ {SOCK_INT, N_("Max"), 100.0f, 0.0f, 0.0f, 0.0f, -100000, 100000},
+ {SOCK_RGBA, N_("Min"), 0.5, 0.5, 0.5, 1.0},
+ {SOCK_RGBA, N_("Max"), 0.5, 0.5, 0.5, 1.0},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_clamp_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_clamp_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeAttributeClamp *data = (NodeAttributeClamp *)MEM_callocN(sizeof(NodeAttributeClamp),
+ __func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->operation = NODE_CLAMP_MINMAX;
+ node->storage = data;
+}
+
+static void geo_node_attribute_clamp_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 3);
+ bNodeSocket *sock_max_vector = sock_min_vector->next;
+ bNodeSocket *sock_min_float = sock_max_vector->next;
+ bNodeSocket *sock_max_float = sock_min_float->next;
+ bNodeSocket *sock_min_int = sock_max_float->next;
+ bNodeSocket *sock_max_int = sock_min_int->next;
+ bNodeSocket *sock_min_color = sock_max_int->next;
+ bNodeSocket *sock_max_color = sock_min_color->next;
+
+ const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)node->storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_min_color, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(sock_max_color, data_type == CD_PROP_COLOR);
+}
+
+namespace blender::nodes {
+
+template<typename T> T clamp_value(const T val, const T min, const T max);
+
+template<> inline float clamp_value(const float val, const float min, const float max)
+{
+ return std::min(std::max(val, min), max);
+}
+
+template<> inline int clamp_value(const int val, const int min, const int max)
+{
+ return std::min(std::max(val, min), max);
+}
+
+template<> inline float3 clamp_value(const float3 val, const float3 min, const float3 max)
+{
+ float3 tmp;
+ tmp.x = std::min(std::max(val.x, min.x), max.x);
+ tmp.y = std::min(std::max(val.y, min.y), max.y);
+ tmp.z = std::min(std::max(val.z, min.z), max.z);
+ return tmp;
+}
+
+template<>
+inline ColorGeometry4f clamp_value(const ColorGeometry4f val,
+ const ColorGeometry4f min,
+ const ColorGeometry4f max)
+{
+ ColorGeometry4f tmp;
+ tmp.r = std::min(std::max(val.r, min.r), max.r);
+ tmp.g = std::min(std::max(val.g, min.g), max.g);
+ tmp.b = std::min(std::max(val.b, min.b), max.b);
+ tmp.a = std::min(std::max(val.a, min.a), max.a);
+ return tmp;
+}
+
+template<typename T>
+static void clamp_attribute(const VArray<T> &inputs,
+ const MutableSpan<T> outputs,
+ const T min,
+ const T max)
+{
+ for (const int i : IndexRange(outputs.size())) {
+ outputs[i] = clamp_value<T>(inputs[i], min, max);
+ }
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef source_name,
+ StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
+ }
+ return ATTR_DOMAIN_POINT;
+}
+
+static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const std::string attribute_name = params.get_input<std::string>("Attribute");
+ const std::string result_name = params.get_input<std::string>("Result");
+
+ if (attribute_name.empty() || result_name.empty()) {
+ return;
+ }
+
+ if (!component.attribute_exists(attribute_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + attribute_name + "\"");
+ return;
+ }
+
+ const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)params.node().storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const AttributeDomain domain = get_result_domain(component, attribute_name, result_name);
+ const int operation = static_cast<int>(storage.operation);
+
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(
+ attribute_name, domain, data_type);
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, domain, data_type);
+
+ if (!attribute_result) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Could not find or create attribute with name \"") +
+ result_name + "\"");
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT3: {
+ float3 min = params.get_input<float3>("Min");
+ float3 max = params.get_input<float3>("Max");
+ if (operation == NODE_CLAMP_RANGE) {
+ if (min.x > max.x) {
+ std::swap(min.x, max.x);
+ }
+ if (min.y > max.y) {
+ std::swap(min.y, max.y);
+ }
+ if (min.z > max.z) {
+ std::swap(min.z, max.z);
+ }
+ }
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max);
+ break;
+ }
+ case CD_PROP_FLOAT: {
+ const float min = params.get_input<float>("Min_001");
+ const float max = params.get_input<float>("Max_001");
+ MutableSpan<float> results = attribute_result.as_span<float>();
+ if (operation == NODE_CLAMP_RANGE && min > max) {
+ clamp_attribute<float>(attribute_input->typed<float>(), results, max, min);
+ }
+ else {
+ clamp_attribute<float>(attribute_input->typed<float>(), results, min, max);
+ }
+ break;
+ }
+ case CD_PROP_INT32: {
+ const int min = params.get_input<int>("Min_002");
+ const int max = params.get_input<int>("Max_002");
+ MutableSpan<int> results = attribute_result.as_span<int>();
+ if (operation == NODE_CLAMP_RANGE && min > max) {
+ clamp_attribute<int>(attribute_input->typed<int>(), results, max, min);
+ }
+ else {
+ clamp_attribute<int>(attribute_input->typed<int>(), results, min, max);
+ }
+ break;
+ }
+ case CD_PROP_COLOR: {
+ ColorGeometry4f min = params.get_input<ColorGeometry4f>("Min_003");
+ ColorGeometry4f max = params.get_input<ColorGeometry4f>("Max_003");
+ if (operation == NODE_CLAMP_RANGE) {
+ if (min.r > max.r) {
+ std::swap(min.r, max.r);
+ }
+ if (min.g > max.g) {
+ std::swap(min.g, max.g);
+ }
+ if (min.b > max.b) {
+ std::swap(min.b, max.b);
+ }
+ if (min.a > max.a) {
+ std::swap(min.a, max.a);
+ }
+ }
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
+ clamp_attribute<ColorGeometry4f>(
+ attribute_input->typed<ColorGeometry4f>(), results, min, max);
+ break;
+ }
+ default: {
+ BLI_assert(false);
+ break;
+ }
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_clamp()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_clamp_in, geo_node_attribute_clamp_out);
+ node_type_init(&ntype, geo_node_attribute_clamp_init);
+ node_type_update(&ntype, geo_node_attribute_clamp_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_clamp_exec;
+ ntype.draw_buttons = geo_node_attribute_clamp_layout;
+ node_type_storage(
+ &ntype, "NodeAttributeClamp", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
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
index 98bf612f589..5293dd8c876 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
@@ -14,13 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
+#include "BLI_task.hh"
#include "BKE_colorband.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_color_ramp_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -42,20 +44,28 @@ static void geo_node_attribute_color_ramp_layout(uiLayout *layout,
namespace blender::nodes {
+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;
+}
+
static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef input_name,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input attribute's domain if it exists. */
- ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name);
- if (input_attribute) {
- return input_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
@@ -71,27 +81,27 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
/* Always output a color attribute for now. We might want to allow users to customize.
* Using the type of an existing attribute could work, but does not have a real benefit
* currently. */
- const CustomDataType result_type = CD_PROP_COLOR;
const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<ColorGeometry4f> attribute_result =
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_in = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> 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_result->get_span_for_write_only<Color4f>();
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_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]);
- }
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]);
+ }
+ });
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
@@ -106,18 +116,13 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
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()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
index 9c5c7e270b1..d8c52d16f41 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
@@ -14,11 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_combine_xyz_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("X")},
@@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -94,27 +94,25 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
}
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> attribute_result =
+ component.attribute_try_get_for_output_only<float3>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_x = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_x = params.get_input_attribute<float>(
"X", component, result_domain, 0.0f);
- FloatReadAttribute attribute_y = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_y = params.get_input_attribute<float>(
"Y", component, result_domain, 0.0f);
- FloatReadAttribute attribute_z = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_z = params.get_input_attribute<float>(
"Z", component, result_domain, 0.0f);
- MutableSpan<float3> results = attribute_result->get_span_for_write_only<float3>();
- for (const int i : results.index_range()) {
+ for (const int i : IndexRange(attribute_result->size())) {
const float x = attribute_x[i];
const float y = attribute_y[i];
const float z = attribute_z[i];
- const float3 result = float3(x, y, z);
- results[i] = result;
+ attribute_result->set(i, {x, y, z});
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
@@ -129,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
index db1a5d18744..57ac68b4cd4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
@@ -14,23 +14,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
-#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_compare_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -91,21 +81,18 @@ static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *n
nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage));
}
-static void do_math_operation(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_math_operation(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const FloatCompareOperation operation,
MutableSpan<bool> span_result)
{
const int size = input_a.size();
- Span<float> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
-
if (try_dispatch_float_math_fl_fl_to_bool(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
- const float a = span_a[i];
- const float b = span_b[i];
+ const float a = input_a[i];
+ const float b = input_b[i];
const bool out = math_function(a, b);
span_result[i] = out;
}
@@ -117,8 +104,8 @@ static void do_math_operation(const FloatReadAttribute &input_a,
BLI_assert(false);
}
-static void do_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -130,8 +117,8 @@ static void do_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -144,22 +131,22 @@ static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a,
+ const VArray<ColorGeometry4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
const float threshold_squared = pow2f(threshold);
const int size = input_a.size();
for (const int i : IndexRange(size)) {
- const Color4f a = input_a[i];
- const Color4f b = input_b[i];
+ const ColorGeometry4f a = input_a[i];
+ const ColorGeometry4f b = input_b[i];
span_result[i] = len_squared_v4v4(a, b) < threshold_squared;
}
}
-static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -171,8 +158,8 @@ static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_not_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -184,8 +171,8 @@ static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_not_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -198,22 +185,22 @@ static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_not_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a,
+ const VArray<ColorGeometry4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
const float threshold_squared = pow2f(threshold);
const int size = input_a.size();
for (const int i : IndexRange(size)) {
- const Color4f a = input_a[i];
- const Color4f b = input_b[i];
+ const ColorGeometry4f a = input_a[i];
+ const ColorGeometry4f b = input_b[i];
span_result[i] = len_squared_v4v4(a, b) >= threshold_squared;
}
}
-static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_not_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -247,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -264,20 +251,19 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
node_storage->operation);
const std::string result_name = params.get_input<std::string>("Result");
- const CustomDataType result_type = CD_PROP_BOOL;
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>(
+ result_name, result_domain);
if (!attribute_result) {
return;
}
const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, input_data_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, input_data_type, nullptr);
if (!attribute_a || !attribute_b) {
@@ -285,7 +271,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
return;
}
- MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>();
+ MutableSpan<bool> result_span = attribute_result.as_span();
/* Use specific types for correct equality operations, but for other operations we use implicit
* conversions and float comparison. In other words, the comparison is not element-wise. */
@@ -293,38 +279,51 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
const float threshold = params.get_input<float>("Threshold");
if (operation == NODE_FLOAT_COMPARE_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
+ attribute_b->typed<ColorGeometry4f>(),
+ threshold,
+ result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
+ attribute_b->typed<ColorGeometry4f>(),
+ threshold,
+ result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
}
else {
- do_math_operation(*attribute_a, *attribute_b, operation, result_span);
+ do_math_operation(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
@@ -339,6 +338,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
index 11d220dd903..7b40456b180 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
@@ -14,11 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_convert_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -35,8 +35,10 @@ static void geo_node_attribute_convert_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
- uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE);
}
static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node)
@@ -44,41 +46,69 @@ static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node
NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert),
__func__);
- data->data_type = CD_PROP_FLOAT;
+ data->data_type = CD_AUTO_FROM_NAME;
data->domain = ATTR_DOMAIN_AUTO;
node->storage = data;
}
namespace blender::nodes {
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef source_name,
- StringRef result_name)
+static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component,
+ const StringRef source_name,
+ const StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return *result_info;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return *source_info;
+ }
+ /* The node won't do anything in this case, but we still have to return a value. */
+ return AttributeMetaData{ATTR_DOMAIN_POINT, CD_PROP_BOOL};
+}
+
+static bool conversion_can_be_skipped(const GeometryComponent &component,
+ const StringRef source_name,
+ const StringRef result_name,
+ const AttributeDomain result_domain,
+ const CustomDataType result_type)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ if (source_name != result_name) {
+ return false;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name);
+ if (!info) {
+ return false;
}
- return ATTR_DOMAIN_POINT;
+ if (info->domain != result_domain) {
+ return false;
+ }
+ if (info->data_type != result_type) {
+ return false;
+ }
+ return true;
}
static void attribute_convert_calc(GeometryComponent &component,
const GeoNodeExecParams &params,
const StringRef source_name,
const StringRef result_name,
- const CustomDataType result_type,
+ const CustomDataType data_type,
const AttributeDomain domain)
{
- const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ?
- get_result_domain(
- component, source_name, result_name) :
- domain;
+ const AttributeMetaData auto_info = get_result_domain_and_type(
+ component, source_name, result_name);
+ const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? auto_info.domain : domain;
+ const CustomDataType result_type = (data_type == CD_AUTO_FROM_NAME) ? auto_info.data_type :
+ data_type;
+
+ if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) {
+ return;
+ }
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
source_name, result_domain, result_type);
if (!source_attribute) {
params.error_message_add(NodeWarningType::Error,
@@ -86,25 +116,22 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
- OutputAttributePtr result_attribute = component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!result_attribute) {
return;
}
- fn::GSpan source_span = source_attribute->get_span();
- fn::GMutableSpan result_span = result_attribute->get_span_for_write_only();
- if (source_span.is_empty() || result_span.is_empty()) {
- return;
- }
+ GVArray_GSpan source_span{*source_attribute};
+ GMutableSpan result_span = result_attribute.as_span();
+
BLI_assert(source_span.size() == result_span.size());
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type);
BLI_assert(cpp_type != nullptr);
cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size());
-
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
@@ -140,6 +167,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
data_type,
domain);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ source_name,
+ result_name,
+ data_type,
+ domain);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
new file mode 100644
index 00000000000..599c9e58e52
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
@@ -0,0 +1,232 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_blenlib.h"
+#include "BLI_task.hh"
+
+#include "BKE_colortools.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_curve_map_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_curve_map_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_curve_map_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ bNode *node = (bNode *)ptr->data;
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ switch (data->data_type) {
+ case CD_PROP_FLOAT:
+ uiTemplateCurveMapping(layout, ptr, "curve_vec", 0, false, false, false, false);
+ break;
+ case CD_PROP_FLOAT3:
+ uiTemplateCurveMapping(layout, ptr, "curve_vec", 'v', false, false, false, false);
+ break;
+ case CD_PROP_COLOR:
+ uiTemplateCurveMapping(layout, ptr, "curve_rgb", 'c', false, false, false, false);
+ break;
+ }
+}
+
+static void geo_node_attribute_curve_map_free_storage(bNode *node)
+{
+ if (node->storage) {
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BKE_curvemapping_free(data->curve_vec);
+ BKE_curvemapping_free(data->curve_rgb);
+ MEM_freeN(node->storage);
+ }
+}
+
+static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntree),
+ bNode *dest_node,
+ const bNode *src_node)
+{
+ dest_node->storage = MEM_dupallocN(src_node->storage);
+ NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage;
+ NodeAttributeCurveMap *dest_data = (NodeAttributeCurveMap *)dest_node->storage;
+ dest_data->curve_vec = BKE_curvemapping_copy(src_data->curve_vec);
+ dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb);
+}
+
+static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap),
+ __func__);
+
+ data->data_type = CD_PROP_FLOAT;
+ data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f);
+ data->curve_vec->cur = 3;
+ data->curve_rgb = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
+ node->storage = data;
+}
+
+static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ /* Set the active curve when data type is changed. */
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ if (data->data_type == CD_PROP_FLOAT) {
+ data->curve_vec->cur = 3;
+ }
+ else if (data->data_type == CD_PROP_FLOAT3) {
+ data->curve_vec->cur = 0;
+ }
+}
+
+namespace blender::nodes {
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef input_name,
+ StringRef result_name)
+{
+ /* Use the domain of the result attribute if it already exists. */
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
+ if (result_attribute) {
+ return result_attribute.domain;
+ }
+
+ /* Otherwise use the input attribute's domain if it exists. */
+ ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name);
+ if (input_attribute) {
+ return input_attribute.domain;
+ }
+
+ return ATTR_DOMAIN_POINT;
+}
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeCurveMap &node_storage = *(NodeAttributeCurveMap *)bnode.storage;
+ const std::string result_name = params.get_input<std::string>("Result");
+ const std::string input_name = params.get_input<std::string>("Attribute");
+
+ const CustomDataType result_type = (CustomDataType)node_storage.data_type;
+ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, result_domain, result_type);
+ if (!attribute_result) {
+ return;
+ }
+
+ switch (result_type) {
+ case CD_PROP_FLOAT: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
+ GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
+ input_name, result_domain, float(0.0f));
+ MutableSpan<float> results = attribute_result.as_span<float>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]);
+ }
+ });
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
+ GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>(
+ input_name, result_domain, float3(0.0f));
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]);
+ }
+ });
+ break;
+ }
+ case CD_PROP_COLOR: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb;
+ GVArray_Typed<ColorGeometry4f> attribute_in =
+ component.attribute_get_for_read<ColorGeometry4f>(
+ input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]);
+ }
+ });
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage;
+ BKE_curvemapping_init(data->curve_vec);
+ BKE_curvemapping_init(data->curve_rgb);
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ 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>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_curve_map()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_curve_map_in, geo_node_attribute_curve_map_out);
+ node_type_update(&ntype, geo_node_attribute_curve_map_update);
+ node_type_init(&ntype, geo_node_attribute_curve_map_init);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ node_type_storage(&ntype,
+ "NodeAttributeCurveMap",
+ geo_node_attribute_curve_map_free_storage,
+ geo_node_attribute_curve_map_copy_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec;
+ ntype.draw_buttons = geo_node_attribute_curve_map_layout;
+ 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
index cbe44c54fb4..51fd65f65fd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
@@ -27,6 +27,11 @@
#include "WM_types.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_fill_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -188,13 +193,12 @@ static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *C, Pointe
draw_input_socket(C, layout, node_ptr, "Value", "data_type");
}
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef attribute_name)
+static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -213,7 +217,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
get_result_domain(component, attribute_name) :
domain;
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output_only(
attribute_name, result_domain, data_type);
if (!attribute) {
return;
@@ -222,38 +226,34 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
switch (data_type) {
case CD_PROP_FLOAT: {
const float value = params.get_input<float>("Value_001");
- MutableSpan<float> attribute_span = attribute->get_span_for_write_only<float>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_FLOAT3: {
const float3 value = params.get_input<float3>("Value");
- MutableSpan<float3> attribute_span = attribute->get_span_for_write_only<float3>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_COLOR: {
- const Color4f value = params.get_input<Color4f>("Value_002");
- MutableSpan<Color4f> attribute_span = attribute->get_span_for_write_only<Color4f>();
- attribute_span.fill(value);
+ const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002");
+ attribute->fill(&value);
break;
}
case CD_PROP_BOOL: {
const bool value = params.get_input<bool>("Value_003");
- MutableSpan<bool> attribute_span = attribute->get_span_for_write_only<bool>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_INT32: {
const int value = params.get_input<int>("Value_004");
- MutableSpan<int> attribute_span = attribute->get_span_for_write_only<int>();
- attribute_span.fill(value);
+ attribute->fill(&value);
+ break;
}
default:
break;
}
- attribute.apply_span_and_save();
+ attribute.save();
}
static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
@@ -268,6 +268,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
new file mode 100644
index 00000000000..40fe675bd6c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
@@ -0,0 +1,437 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_math_base_safe.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_map_range_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {SOCK_FLOAT, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("From Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("To Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("From Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("To Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_BOOLEAN, N_("Clamp")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_map_range_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_map_range_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMapRange *data = (NodeAttributeMapRange *)MEM_callocN(sizeof(NodeAttributeMapRange),
+ __func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->interpolation_type = NODE_MAP_RANGE_LINEAR;
+
+ node->storage = data;
+}
+static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node->storage;
+
+ bNodeSocket *sock_from_min_float = (bNodeSocket *)BLI_findlink(&node->inputs, 3);
+ bNodeSocket *sock_from_max_float = sock_from_min_float->next;
+ bNodeSocket *sock_to_min_float = sock_from_max_float->next;
+ bNodeSocket *sock_to_max_float = sock_to_min_float->next;
+ bNodeSocket *sock_steps_float = sock_to_max_float->next;
+
+ bNodeSocket *sock_from_min_vector = sock_steps_float->next;
+ bNodeSocket *sock_from_max_vector = sock_from_min_vector->next;
+ bNodeSocket *sock_to_min_vector = sock_from_max_vector->next;
+ bNodeSocket *sock_to_max_vector = sock_to_min_vector->next;
+ bNodeSocket *sock_steps_vector = sock_to_max_vector->next;
+
+ bNodeSocket *sock_clamp = sock_steps_vector->next;
+
+ const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
+
+ nodeSetSocketAvailability(sock_clamp,
+ node_storage.interpolation_type == NODE_MAP_RANGE_LINEAR ||
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+
+ nodeSetSocketAvailability(sock_from_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_from_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_to_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_to_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_steps_float,
+ data_type == CD_PROP_FLOAT &&
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+
+ nodeSetSocketAvailability(sock_from_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_from_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_to_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_to_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_steps_vector,
+ data_type == CD_PROP_FLOAT3 &&
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+}
+
+namespace blender::nodes {
+
+static float map_linear(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ /* First we calculate a fraction that measures how far along
+ * the [min_from, max_from] interval the value lies.
+ *
+ * value
+ * min_from [------>|------------------------] max_from
+ * factor (e.g. 0.25)
+ *
+ * Then to find where the value is mapped, we add the same fraction
+ * of the [min_to, max_to] interval to min_to.
+ *
+ * min_to [--->|-----------] max_to
+ * v
+ * min_to + (max_to - min_to) * factor
+ */
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ return min_to + factor * (max_to - min_to);
+}
+
+static float map_stepped(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to,
+ const float steps)
+{
+ /* First the factor is calculated here in the same way as for the linear mapping.
+ *
+ * Then the factor is mapped to multiples of 1.0 / steps.
+ * This is best understood with a few examples. Assume steps == 3.
+ * ____________________________________
+ * | factor | * 4.0 | floor() | / 3.0 |
+ * |--------|-------|---------|-------|
+ * | 0.0 | 0.0 | 0.0 | 0.0 |
+ * | 0.1 | 0.4 | 0.0 | 0.0 |
+ * | 0.25 | 1.0 | 1.0 | 0.333 |
+ * | 0.45 | 1.8 | 1.0 | 0.333 |
+ * | 0.5 | 2.0 | 2.0 | 0.666 |
+ * | 0.55 | 2.2 | 2.0 | 0.666 |
+ * | 0.999 | 3.999 | 3.0 | 1.0 |
+ * | 1.0 | 4.0 | 4.0 | 1.333 |
+ * ------------------------------------
+ * Note that the factor is not always mapped the closest multiple of 1.0 /steps.
+ */
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_mapped = safe_divide(floorf(factor * (steps + 1.0f)), steps);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static float smoothstep_polynomial(float x)
+{
+ /* This polynomial is only meant to be used for the [0, 1] range. */
+ return (3.0f - 2.0f * x) * (x * x);
+}
+
+static float map_smoothstep(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_clamped = std::clamp(factor, 0.0f, 1.0f);
+ const float factor_mapped = smoothstep_polynomial(factor_clamped);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static float smootherstep_polynomial(float x)
+{
+ /* This polynomial is only meant to be used for the [0, 1] range. */
+ return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f);
+}
+
+static float map_smootherstep(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_clamped = std::clamp(factor, 0.0f, 1.0f);
+ const float factor_mapped = smootherstep_polynomial(factor_clamped);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static void map_range_float(const VArray<float> &attribute_input,
+ MutableSpan<float> results,
+ const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const int interpolation_type = node_storage.interpolation_type;
+ const float min_from = params.get_input<float>("From Min");
+ const float max_from = params.get_input<float>("From Max");
+ const float min_to = params.get_input<float>("To Min");
+ const float max_to = params.get_input<float>("To Max");
+
+ VArray_Span<float> span{attribute_input};
+
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ parallel_for(span.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_STEPPED: {
+ const float steps = params.get_input<float>("Steps");
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ }
+
+ if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) &&
+ params.get_input<bool>("Clamp")) {
+ /* Users can specify min_to > max_to, but clamping expects min < max. */
+ const float clamp_min = min_to < max_to ? min_to : max_to;
+ const float clamp_max = min_to < max_to ? max_to : min_to;
+
+ parallel_for(results.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = std::clamp(results[i], clamp_min, clamp_max);
+ }
+ });
+ }
+}
+
+static void map_range_float3(const VArray<float3> &attribute_input,
+ const MutableSpan<float3> results,
+ const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const int interpolation_type = node_storage.interpolation_type;
+ const float3 min_from = params.get_input<float3>("From Min_001");
+ const float3 max_from = params.get_input<float3>("From Max_001");
+ const float3 min_to = params.get_input<float3>("To Min_001");
+ const float3 max_to = params.get_input<float3>("To Max_001");
+
+ VArray_Span<float3> span{attribute_input};
+
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_STEPPED: {
+ const float3 steps = params.get_input<float3>("Steps_001");
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_stepped(
+ span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
+ results[i].y = map_stepped(
+ span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
+ results[i].z = map_stepped(
+ span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ }
+
+ if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) &&
+ params.get_input<bool>("Clamp")) {
+ /* Users can specify min_to > max_to, but clamping expects min < max. */
+ float3 clamp_min;
+ float3 clamp_max;
+ clamp_min.x = min_to.x < max_to.x ? min_to.x : max_to.x;
+ clamp_max.x = min_to.x < max_to.x ? max_to.x : min_to.x;
+ clamp_min.y = min_to.y < max_to.y ? min_to.y : max_to.y;
+ clamp_max.y = min_to.y < max_to.y ? max_to.y : min_to.y;
+ clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z;
+ clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z;
+
+ for (int i : results.index_range()) {
+ clamp_v3_v3v3(results[i], clamp_min, clamp_max);
+ }
+ }
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef source_name,
+ StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
+ }
+ return ATTR_DOMAIN_POINT;
+}
+
+static void map_range_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const std::string input_name = params.get_input<std::string>("Attribute");
+ const std::string result_name = params.get_input<std::string>("Result");
+
+ if (input_name.empty() || result_name.empty()) {
+ return;
+ }
+
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
+
+ const AttributeDomain domain = get_result_domain(component, input_name, result_name);
+
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
+
+ if (!attribute_input) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + input_name + "\"");
+ return;
+ }
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, domain, data_type);
+ if (!attribute_result) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Could not find or create attribute with name \"") +
+ result_name + "\"");
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params);
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ map_range_float3(
+ attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params);
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_map_range()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_map_range_in, geo_node_attribute_map_range_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_map_range_exec;
+ node_type_init(&ntype, geo_node_attribute_map_range_init);
+ node_type_update(&ntype, geo_node_attribute_map_range_update);
+ node_type_storage(
+ &ntype, "NodeAttributeMapRange", node_free_standard_storage, node_copy_standard_storage);
+ ntype.draw_buttons = fn_attribute_map_range_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index 8c6d49e322c..5b78b4cd39e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -14,23 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
-#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -163,46 +155,52 @@ static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node
operation_use_input_c(operation));
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
- Span<float> span_c,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
+ const VArray<float> &span_c,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_input,
+static void do_math_operation(const VArray<float> &span_input,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_input[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_input[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
@@ -214,9 +212,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -238,56 +236,39 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
const std::string result_name = params.get_input<std::string>("Result");
/* The result type of this node is always float. */
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result =
+ component.attribute_try_get_for_output_only<float>(result_name, result_domain);
if (!attribute_result) {
return;
}
- ReadAttributePtr attribute_a = params.get_input_attribute(
- "A", component, result_domain, result_type, nullptr);
- if (!attribute_a) {
- return;
- }
+ GVArray_Typed<float> attribute_a = params.get_input_attribute<float>(
+ "A", component, result_domain, 0.0f);
+
+ MutableSpan<float> result_span = attribute_result.as_span();
- /* Note that passing the data with `get_span<float>()` works
+ /* Note that passing the data with `get_internal_span<float>()` works
* because the attributes were accessed with #CD_PROP_FLOAT. */
if (operation_use_input_b(operation)) {
- ReadAttributePtr attribute_b = params.get_input_attribute(
- "B", component, result_domain, result_type, nullptr);
- if (!attribute_b) {
- return;
- }
+ GVArray_Typed<float> attribute_b = params.get_input_attribute<float>(
+ "B", component, result_domain, 0.0f);
if (operation_use_input_c(operation)) {
- ReadAttributePtr attribute_c = params.get_input_attribute(
- "C", component, result_domain, result_type, nullptr);
- if (!attribute_c) {
- return;
- }
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_c->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ GVArray_Typed<float> attribute_c = params.get_input_attribute<float>(
+ "C", component, result_domain, 0.0f);
+ do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation);
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, attribute_b, result_span, operation);
}
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, result_span, operation);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
@@ -302,6 +283,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
index 9d8cd3dfa82..a6bd6c0ee32 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "BKE_material.h"
#include "DNA_material_types.h"
@@ -57,73 +59,110 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
+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);
+}
+
static void do_mix_operation_float(const int blend_mode,
- const FloatReadAttribute &factors,
- const FloatReadAttribute &inputs_a,
- const FloatReadAttribute &inputs_b,
- FloatWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float> &inputs_a,
+ const VArray<float> &inputs_b,
+ VMutableArray<float> &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.x;
- results.set(i, result);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ 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.x;
+ 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 VArray<float> &factors,
+ const VArray<float3> &inputs_a,
+ const VArray<float3> &inputs_b,
+ VMutableArray<float3> &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);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ 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 VArray<float> &factors,
+ const VArray<ColorGeometry4f> &inputs_a,
+ const VArray<ColorGeometry4f> &inputs_b,
+ VMutableArray<ColorGeometry4f> &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);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ ColorGeometry4f a = inputs_a[i];
+ const ColorGeometry4f 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,
- const ReadAttribute &attribute_a,
- const ReadAttribute &attribute_b,
- WriteAttribute &attribute_result)
+ const VArray<float> &attribute_factor,
+ const GVArray &attribute_a,
+ const GVArray &attribute_b,
+ GVMutableArray &attribute_result)
{
if (result_type == CD_PROP_FLOAT) {
- do_mix_operation_float(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float>(),
+ attribute_b.typed<float>(),
+ attribute_result.typed<float>());
}
else if (result_type == CD_PROP_FLOAT3) {
- do_mix_operation_float3(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float3(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_result.typed<float3>());
}
else if (result_type == CD_PROP_COLOR) {
- do_mix_operation_color4f(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_color4f(blend_mode,
+ attribute_factor,
+ attribute_a.typed<ColorGeometry4f>(),
+ attribute_b.typed<ColorGeometry4f>(),
+ attribute_result.typed<ColorGeometry4f>());
}
}
@@ -132,9 +171,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -158,17 +197,17 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_factor = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>(
"Factor", component, result_domain, 0.5f);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, result_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, result_type, nullptr);
do_mix_operation(result_type,
@@ -192,32 +231,13 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), 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()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
index f09a9bf056e..9c22b7fa87f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
@@ -62,7 +62,7 @@ namespace blender::nodes {
static void proximity_calc(MutableSpan<float> distance_span,
MutableSpan<float3> location_span,
- Span<float3> positions,
+ const VArray<float3> &positions,
BVHTreeFromMesh &tree_data_mesh,
BVHTreeFromPointCloud &tree_data_pointcloud,
const bool bvh_mesh_success,
@@ -169,19 +169,18 @@ static void attribute_calc_proximity(GeometryComponent &component,
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
const std::string distance_attribute_name = params.get_input<std::string>("Distance");
- OutputAttributePtr distance_attribute = component.attribute_try_get_for_output(
- distance_attribute_name, result_domain, CD_PROP_FLOAT);
+ OutputAttribute_Typed<float> distance_attribute =
+ component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain);
const std::string location_attribute_name = params.get_input<std::string>("Position");
- OutputAttributePtr location_attribute = component.attribute_try_get_for_output(
- location_attribute_name, result_domain, CD_PROP_FLOAT3);
-
- ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
- BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> location_attribute =
+ component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain);
+ ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position");
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
}
+ BLI_assert(position_attribute.varray->type().is<float3>());
const bNode &node = params.node();
const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)
@@ -204,18 +203,15 @@ static void attribute_calc_proximity(GeometryComponent &component,
tree_data_pointcloud);
}
- Span<float3> position_span = position_attribute->get_span<float3>();
-
- MutableSpan<float> distance_span = distance_attribute ?
- distance_attribute->get_span_for_write_only<float>() :
- MutableSpan<float>();
- MutableSpan<float3> location_span = location_attribute ?
- location_attribute->get_span_for_write_only<float3>() :
- MutableSpan<float3>();
+ GVArray_Typed<float3> positions{*position_attribute.varray};
+ MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() :
+ MutableSpan<float>();
+ MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() :
+ MutableSpan<float3>();
proximity_calc(distance_span,
location_span,
- position_span,
+ positions,
tree_data_mesh,
tree_data_pointcloud,
bvh_mesh_success,
@@ -231,10 +227,10 @@ static void attribute_calc_proximity(GeometryComponent &component,
}
if (distance_attribute) {
- distance_attribute.apply_span_and_save();
+ distance_attribute.save();
}
if (location_attribute) {
- location_attribute.apply_span_and_save();
+ location_attribute.save();
}
}
@@ -257,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
attribute_calc_proximity(
geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_calc_proximity(
+ geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 27c35da7d37..286411b7d28 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -14,16 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_hash.h"
#include "BLI_rand.hh"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_randomize_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
@@ -128,28 +126,36 @@ static void randomize_attribute(MutableSpan<T> span,
/* The operations could be templated too, but it doesn't make the code much shorter. */
switch (operation) {
case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] + random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] + random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] - random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] - random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] * random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] * random_value;
+ }
+ });
break;
default:
BLI_assert(false);
@@ -164,10 +170,12 @@ static void randomize_attribute_bool(MutableSpan<bool> span,
{
BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE);
UNUSED_VARS_NDEBUG(operation);
- for (const int i : span.index_range()) {
- const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
+ span[i] = random_value;
+ }
+ });
}
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
@@ -176,15 +184,17 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
const int domain_size = component.attribute_domain_size(domain);
/* 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);
+ GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain);
Array<uint32_t> hashes(domain_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] = cpp_type.hash(items[i]);
- }
+ const CPPType &cpp_type = hash_attribute->type();
+ GVArray_GSpan items{*hash_attribute};
+ parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ hashes[i] = cpp_type.hash(items[i]);
+ }
+ });
}
else {
/* If there is no "id" attribute for per-point variation, just create it here. */
@@ -199,12 +209,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef attribute_name)
+ const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input domain chosen in the interface. */
@@ -231,15 +241,13 @@ static void randomize_attribute_on_component(GeometryComponent &component,
const AttributeDomain domain = get_result_domain(component, params, attribute_name);
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output(
attribute_name, domain, data_type);
if (!attribute) {
return;
}
- fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ?
- attribute->get_span_for_write_only() :
- attribute->get_span();
+ GMutableSpan span = attribute.as_span();
Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain);
@@ -272,8 +280,8 @@ static void randomize_attribute_on_component(GeometryComponent &component,
}
}
- attribute.apply_span_and_save();
-} // namespace blender::nodes
+ attribute.save();
+}
static void geo_node_random_attribute_exec(GeoNodeExecParams params)
{
@@ -307,6 +315,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
operation,
seed);
}
+ if (geometry_set.has<CurveComponent>()) {
+ randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ attribute_name,
+ data_type,
+ operation,
+ seed);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index 837f0c3629a..e4f3230ebb9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
remove_attribute(
geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
}
+ if (geometry_set.has<CurveComponent>()) {
+ remove_attribute(
+ geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
index d0b2595b5b9..d6b1ad3e9e0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_compiler_attrs.h"
+#include "BLI_task.hh"
#include "DNA_texture_types.h"
@@ -29,6 +30,7 @@
static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_TEXTURE, N_("Texture")},
{SOCK_STRING, N_("Mapping")},
{SOCK_STRING, N_("Result")},
{-1, ""},
@@ -39,29 +41,22 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = {
{-1, ""},
};
-static void geo_node_attribute_sample_texture_layout(uiLayout *layout,
- bContext *C,
- PointerRNA *ptr)
-{
- uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
-}
-
namespace blender::nodes {
static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef result_attribute_name,
- StringRef map_attribute_name)
+ const StringRef result_name,
+ const StringRef map_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the name of the map attribute. */
- ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name);
- if (map_attribute) {
- return map_attribute->domain();
+ std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name);
+ if (map_info) {
+ return map_info->domain;
}
/* The node won't execute in this case, but we still have to return a value. */
@@ -70,8 +65,7 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams &params)
{
- const bNode &node = params.node();
- Tex *texture = reinterpret_cast<Tex *>(node.id);
+ Tex *texture = params.get_input<Tex *>("Texture");
if (texture == nullptr) {
return;
}
@@ -85,25 +79,29 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
const AttributeDomain result_domain = get_result_domain(
component, result_attribute_name, mapping_name);
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
- result_attribute_name, result_domain, CD_PROP_COLOR);
+ OutputAttribute_Typed<ColorGeometry4f> attribute_out =
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name,
+ result_domain);
if (!attribute_out) {
return;
}
- Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
mapping_name, result_domain, {0, 0, 0});
- MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>();
- for (const int i : IndexRange(mapping_attribute.size())) {
- TexResult texture_result = {0};
- const float3 position = mapping_attribute[i];
- /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
- const float3 remapped_position = position * 2.0f - float3(1.0f);
- BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
- colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
- }
- attribute_out.apply_span_and_save();
+ MutableSpan<ColorGeometry4f> colors = attribute_out.as_span();
+ parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ TexResult texture_result = {0};
+ const float3 position = mapping_attribute[i];
+ /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
+ const float3 remapped_position = position * 2.0f - float3(1.0f);
+ BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
+ colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
+ }
+ });
+
+ attribute_out.save();
}
static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
@@ -118,6 +116,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
@@ -137,6 +138,5 @@ void register_node_type_geo_sample_texture()
node_type_socket_templates(
&ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec;
- ntype.draw_buttons = geo_node_attribute_sample_texture_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
index 55b933e8582..137a72bb707 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
@@ -14,13 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "DNA_material_types.h"
-
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_separate_xyz_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Vector")},
@@ -73,23 +71,23 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef result_name_x,
- StringRef result_name_y,
- StringRef result_name_z)
+ const StringRef name_x,
+ const StringRef name_y,
+ const StringRef name_z)
{
/* Use the highest priority domain from any existing attribute outputs. */
Vector<AttributeDomain, 3> output_domains;
- ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x);
- ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y);
- ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z);
- if (attribute_x) {
- output_domains.append(attribute_x->domain());
+ std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x);
+ std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y);
+ std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z);
+ if (info_x) {
+ output_domains.append(info_x->domain);
}
- if (attribute_y) {
- output_domains.append(attribute_y->domain());
+ if (info_y) {
+ output_domains.append(info_y->domain);
}
- if (attribute_z) {
- output_domains.append(attribute_z->domain());
+ if (info_z) {
+ output_domains.append(info_z->domain);
}
if (output_domains.size() > 0) {
return bke::attribute_domain_highest_priority(output_domains);
@@ -109,37 +107,32 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
}
/* The node is only for float3 to float conversions. */
- const CustomDataType input_type = CD_PROP_FLOAT3;
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, result_name_x, result_name_y, result_name_z);
- ReadAttributePtr attribute_input = params.get_input_attribute(
- "Vector", component, result_domain, input_type, nullptr);
- if (!attribute_input) {
- return;
- }
- const Span<float3> input_span = attribute_input->get_span<float3>();
+ GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>(
+ "Vector", component, result_domain, {0, 0, 0});
+ VArray_Span<float3> input_span{*attribute_input};
- OutputAttributePtr attribute_result_x = component.attribute_try_get_for_output(
- result_name_x, result_domain, result_type);
- OutputAttributePtr attribute_result_y = component.attribute_try_get_for_output(
- result_name_y, result_domain, result_type);
- OutputAttributePtr attribute_result_z = component.attribute_try_get_for_output(
- result_name_z, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result_x =
+ component.attribute_try_get_for_output_only<float>(result_name_x, result_domain);
+ OutputAttribute_Typed<float> attribute_result_y =
+ component.attribute_try_get_for_output_only<float>(result_name_y, result_domain);
+ OutputAttribute_Typed<float> attribute_result_z =
+ component.attribute_try_get_for_output_only<float>(result_name_z, result_domain);
/* Only extract the components for the outputs with a given attribute. */
if (attribute_result_x) {
- extract_input(0, input_span, attribute_result_x->get_span_for_write_only<float>());
- attribute_result_x.apply_span_and_save();
+ extract_input(0, input_span, attribute_result_x.as_span());
+ attribute_result_x.save();
}
if (attribute_result_y) {
- extract_input(1, input_span, attribute_result_y->get_span_for_write_only<float>());
- attribute_result_y.apply_span_and_save();
+ extract_input(1, input_span, attribute_result_y.as_span());
+ attribute_result_y.save();
}
if (attribute_result_z) {
- extract_input(2, input_span, attribute_result_z->get_span_for_write_only<float>());
- attribute_result_z.apply_span_and_save();
+ extract_input(2, input_span, attribute_result_z.as_span());
+ attribute_result_z.save();
}
}
@@ -155,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
new file mode 100644
index 00000000000..4b677dc5c82
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
@@ -0,0 +1,597 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_kdopbvh.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_transfer_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_GEOMETRY, N_("Source Geometry")},
+ {SOCK_STRING, N_("Source")},
+ {SOCK_STRING, N_("Destination")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_transfer_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_transfer_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE);
+ uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN(
+ sizeof(NodeGeometryAttributeTransfer), __func__);
+ data->domain = ATTR_DOMAIN_AUTO;
+ node->storage = data;
+}
+
+static void get_result_domain_and_data_type(const GeometrySet &src_geometry,
+ const GeometryComponent &dst_component,
+ const StringRef attribute_name,
+ CustomDataType *r_data_type,
+ AttributeDomain *r_domain)
+{
+ Vector<CustomDataType> data_types;
+ Vector<AttributeDomain> domains;
+
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ if (pointcloud_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = pointcloud_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ if (mesh_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = mesh_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ *r_data_type = bke::attribute_data_type_highest_complexity(data_types);
+
+ if (dst_component.type() == GEO_COMPONENT_TYPE_POINT_CLOUD) {
+ *r_domain = ATTR_DOMAIN_POINT;
+ }
+ else {
+ *r_domain = bke::attribute_domain_highest_priority(domains);
+ }
+}
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty());
+ BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty());
+ BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty());
+
+ for (const int i : positions.index_range()) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ if (!r_indices.is_empty()) {
+ r_indices[i] = nearest.index;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = nearest.co;
+ }
+ }
+}
+
+static void get_closest_pointcloud_points(const PointCloud &pointcloud,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq)
+{
+ BLI_assert(positions.size() == r_indices.size());
+ BLI_assert(pointcloud.totpoint > 0);
+
+ BVHTreeFromPointCloud tree_data;
+ BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2);
+
+ for (const int i : positions.index_range()) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ r_indices[i] = nearest.index;
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+
+ free_bvhtree_from_pointcloud(&tree_data);
+}
+
+static void get_closest_mesh_points(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_point_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totvert > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_point_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_edges(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_edge_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totedge > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_EDGES, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_edge_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_looptris(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_looptri_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_looptri_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_polygons(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_poly_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+
+ Array<int> looptri_indices(positions.size());
+ get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions);
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ for (const int i : positions.index_range()) {
+ const MLoopTri &looptri = looptris[looptri_indices[i]];
+ r_poly_indices[i] = looptri.poly;
+ }
+}
+
+/* The closest corner is defined to be the closest corner on the closest face. */
+static void get_closest_mesh_corners(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_corner_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totloop > 0);
+ Array<int> poly_indices(positions.size());
+ get_closest_mesh_polygons(mesh, positions, poly_indices, {}, {});
+
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const int poly_index = poly_indices[i];
+ const MPoly &poly = mesh.mpoly[poly_index];
+
+ /* Find the closest vertex in the polygon. */
+ float min_distance_sq = FLT_MAX;
+ const MVert *closest_mvert;
+ int closest_loop_index = 0;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ const int vertex_index = loop.v;
+ const MVert &mvert = mesh.mvert[vertex_index];
+ const float distance_sq = float3::distance_squared(position, mvert.co);
+ if (distance_sq < min_distance_sq) {
+ min_distance_sq = distance_sq;
+ closest_loop_index = loop_index;
+ closest_mvert = &mvert;
+ }
+ }
+ if (!r_corner_indices.is_empty()) {
+ r_corner_indices[i] = closest_loop_index;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = closest_mvert->co;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = min_distance_sq;
+ }
+ }
+}
+
+static void get_barycentric_coords(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> positions,
+ const MutableSpan<float3> r_bary_coords)
+{
+ BLI_assert(r_bary_coords.size() == positions.size());
+ BLI_assert(r_bary_coords.size() == looptri_indices.size());
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : r_bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ interp_weights_tri_v3(r_bary_coords[i],
+ mesh.mvert[v0_index].co,
+ mesh.mvert[v1_index].co,
+ mesh.mvert[v2_index].co,
+ positions[i]);
+ }
+}
+
+static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const int tot_samples = dst_positions.size();
+ const MeshComponent *component = src_geometry.get_component_for_read<MeshComponent>();
+ if (component == nullptr) {
+ return;
+ }
+ const Mesh *mesh = component->get_for_read();
+ if (mesh == nullptr) {
+ return;
+ }
+ if (mesh->totpoly == 0) {
+ return;
+ }
+ ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type);
+ if (!src_attribute) {
+ return;
+ }
+
+ /* Find closest points on the mesh surface. */
+ Array<int> looptri_indices(tot_samples);
+ Array<float3> positions(tot_samples);
+ get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions);
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+ GMutableSpan dst_span = dst_attribute.as_span();
+ Array<float3> bary_coords;
+
+ /* Compute barycentric coordinates only when they are needed. */
+ if (src_attribute.domain != ATTR_DOMAIN_FACE) {
+ bary_coords.reinitialize(tot_samples);
+ get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords);
+ }
+ /* Interpolate the source attribute on the surface. */
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ bke::mesh_surface_sample::sample_point_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ bke::mesh_surface_sample::sample_face_attribute(
+ *mesh, looptri_indices, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ bke::mesh_surface_sample::sample_corner_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Not yet supported. */
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ dst_attribute.save();
+}
+
+static void transfer_attribute_nearest(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ /* Get pointcloud data from geometry. */
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ const PointCloud *pointcloud = pointcloud_component ? pointcloud_component->get_for_read() :
+ nullptr;
+
+ /* Get mesh data from geometry. */
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ const Mesh *mesh = mesh_component ? mesh_component->get_for_read() : nullptr;
+
+ const int tot_samples = dst_positions.size();
+
+ Array<int> pointcloud_indices;
+ Array<float> pointcloud_distances_sq;
+ bool use_pointcloud = false;
+
+ /* Depending on where what domain the source attribute lives, these indices are either vertex,
+ * corner, edge or polygon indices. */
+ Array<int> mesh_indices;
+ Array<float> mesh_distances_sq;
+ bool use_mesh = false;
+
+ /* If there is a pointcloud, find the closest points. */
+ if (pointcloud != nullptr && pointcloud->totpoint > 0) {
+ if (pointcloud_component->attribute_exists(src_name)) {
+ use_pointcloud = true;
+ pointcloud_indices.reinitialize(tot_samples);
+ pointcloud_distances_sq.reinitialize(tot_samples);
+ get_closest_pointcloud_points(
+ *pointcloud, dst_positions, pointcloud_indices, pointcloud_distances_sq);
+ }
+ }
+
+ /* If there is a mesh, find the closest mesh elements. */
+ if (mesh != nullptr) {
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name);
+ if (src_attribute) {
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ if (mesh->totvert > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_points(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ if (mesh->totedge > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_edges(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ if (mesh->totpoly > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_polygons(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ if (!use_pointcloud && !use_mesh) {
+ return;
+ }
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+
+ /* Create a buffer for intermediate values. */
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+
+ if (use_mesh && use_pointcloud) {
+ /* When there is a mesh and a pointcloud, we still have to check whether a pointcloud point or
+ * a mesh element is closer to every point. */
+ ReadAttributeLookup pointcloud_src_attribute =
+ pointcloud_component->attribute_try_get_for_read(src_name, data_type);
+ ReadAttributeLookup mesh_src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
+ /* Pointcloud point is closer. */
+ const int index = pointcloud_indices[i];
+ pointcloud_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ else {
+ /* Mesh element is closer. */
+ const int index = mesh_indices[i];
+ mesh_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ }
+ else if (use_pointcloud) {
+ /* The source geometry only has a pointcloud. */
+ ReadAttributeLookup src_attribute = pointcloud_component->attribute_try_get_for_read(
+ src_name, data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = pointcloud_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ else if (use_mesh) {
+ /* The source geometry only has a mesh. */
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = mesh_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+
+ dst_attribute.save();
+}
+
+static void transfer_attribute(const GeoNodeExecParams &params,
+ const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const NodeGeometryAttributeTransfer &storage =
+ *(const NodeGeometryAttributeTransfer *)params.node().storage;
+ const GeometryNodeAttributeTransferMapMode mapping = (GeometryNodeAttributeTransferMapMode)
+ storage.mapping;
+ const AttributeDomain input_domain = (AttributeDomain)storage.domain;
+
+ CustomDataType data_type;
+ AttributeDomain auto_domain;
+ get_result_domain_and_data_type(src_geometry, dst_component, src_name, &data_type, &auto_domain);
+ const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain :
+ input_domain;
+
+ GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
+ "position", dst_domain, {0, 0, 0});
+
+ switch (mapping) {
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: {
+ transfer_attribute_nearest_face_interpolated(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
+ transfer_attribute_nearest(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ }
+}
+
+static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
+{
+ GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry");
+ const std::string src_attribute_name = params.extract_input<std::string>("Source");
+ const std::string dst_attribute_name = params.extract_input<std::string>("Destination");
+
+ if (src_attribute_name.empty() || dst_attribute_name.empty()) {
+ params.set_output("Geometry", dst_geometry_set);
+ return;
+ }
+
+ dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
+ src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
+
+ if (dst_geometry_set.has<MeshComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<MeshComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+ if (dst_geometry_set.has<PointCloudComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<PointCloudComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+
+ params.set_output("Geometry", dst_geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_transfer()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_transfer_in, geo_node_attribute_transfer_out);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init);
+ node_type_storage(&ntype,
+ "NodeGeometryAttributeTransfer",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec;
+ ntype.draw_buttons = geo_node_attribute_transfer_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
index ee2fd763ead..5a5fc099779 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -14,23 +14,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_vector_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -66,8 +59,11 @@ static bool operation_use_input_b(const NodeVectorMathOperation operation)
static bool operation_use_input_c(const NodeVectorMathOperation operation)
{
- return ELEM(
- operation, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD);
+ return ELEM(operation,
+ NODE_VECTOR_MATH_WRAP,
+ NODE_VECTOR_MATH_REFRACT,
+ NODE_VECTOR_MATH_FACEFORWARD,
+ NODE_VECTOR_MATH_MULTIPLY_ADD);
}
static CustomDataType operation_get_read_type_b(const NodeVectorMathOperation operation)
@@ -129,6 +125,7 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op
case NODE_VECTOR_MATH_TANGENT:
case NODE_VECTOR_MATH_REFRACT:
case NODE_VECTOR_MATH_FACEFORWARD:
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
return CD_PROP_FLOAT3;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
@@ -182,196 +179,210 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
operation_use_input_c(operation));
}
-static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const Float3ReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float3> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float3> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float3> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result};
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const FloatReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const FloatReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float3 out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float3 out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
@@ -384,9 +395,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
if (result_attribute) {
- return result_attribute->domain();
+ return result_attribute.domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -420,13 +431,13 @@ static void attribute_vector_math_calc(GeometryComponent &component,
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
if (!attribute_a) {
return;
}
- ReadAttributePtr attribute_b;
- ReadAttributePtr attribute_c;
+ GVArrayPtr attribute_b;
+ GVArrayPtr attribute_c;
if (use_input_b) {
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
if (!attribute_b) {
@@ -441,7 +452,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
}
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
@@ -459,17 +470,27 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
- do_math_operation_fl3_fl3_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
- do_math_operation_fl3_fl3_to_fl(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float>(),
+ operation);
break;
case NODE_VECTOR_MATH_LENGTH:
- do_math_operation_fl3_to_fl(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl(
+ attribute_a->typed<float3>(), attribute_result->typed<float>(), operation);
break;
case NODE_VECTOR_MATH_SCALE:
- do_math_operation_fl3_fl_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_NORMALIZE:
case NODE_VECTOR_MATH_FLOOR:
@@ -479,16 +500,24 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_SINE:
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
- do_math_operation_fl3_to_fl3(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl3(
+ attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation);
break;
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_FACEFORWARD:
- do_math_operation_fl3_fl3_fl3_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_REFRACT:
- do_math_operation_fl3_fl3_fl_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
}
attribute_result.save();
@@ -507,6 +536,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
new file mode 100644
index 00000000000..4d568ab5c3a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
@@ -0,0 +1,352 @@
+/*
+ * 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_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_attribute_vector_rotate_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Vector")},
+ {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+ {SOCK_STRING, N_("Center")},
+ {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {SOCK_STRING, N_("Axis")},
+ {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_XYZ, PROP_NONE},
+ {SOCK_STRING, N_("Angle")},
+ {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE},
+ {SOCK_STRING, N_("Rotation")},
+ {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
+ {SOCK_BOOLEAN, N_("Invert")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_vector_rotate_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_vector_rotate_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ bNode *node = (bNode *)ptr->data;
+ const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
+ node_storage.mode;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiLayout *column = uiLayoutColumn(layout, false);
+
+ uiItemR(column, ptr, "rotation_mode", 0, "", ICON_NONE);
+
+ uiItemR(column, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE);
+ uiItemR(column, ptr, "input_type_center", 0, IFACE_("Center"), ICON_NONE);
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS) {
+ uiItemR(column, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE);
+ }
+ if (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ uiItemR(column, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE);
+ }
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ uiItemR(column, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE);
+ }
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
+ node_storage->mode;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+ update_attribute_input_socket_availabilities(
+ *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Axis",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
+ (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Angle",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
+ (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Rotation",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
+ (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
+}
+
+static float3 vector_rotate_around_axis(const float3 vector,
+ const float3 center,
+ const float3 axis,
+ const float angle)
+{
+ float3 result = vector - center;
+ float mat[3][3];
+ axis_angle_to_mat3(mat, axis, angle);
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
+static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN(
+ sizeof(NodeAttributeVectorRotate), __func__);
+
+ node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS;
+ node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node_storage->input_type_center = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static float3 vector_rotate_euler(const float3 vector,
+ const float3 center,
+ const float3 rotation,
+ const bool invert)
+{
+ float mat[3][3];
+ float3 result = vector - center;
+ eul_to_mat3(mat, rotation);
+ if (invert) {
+ invert_m3(mat);
+ }
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
+static void do_vector_rotate_around_axis(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const VArray<float3> &axis,
+ const VArray<float> &angle,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float3> span_axis{axis};
+ VArray_Span<float> span_angle{angle};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ float angle = (invert) ? -span_angle[i] : span_angle[i];
+ results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle);
+ }
+ });
+}
+
+static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const float3 axis,
+ const VArray<float> &angle,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float> span_angle{angle};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ float angle = (invert) ? -span_angle[i] : span_angle[i];
+ results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle);
+ }
+ });
+}
+
+static void do_vector_rotate_euler(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const VArray<float3> &rotation,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float3> span_rotation{rotation};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert);
+ }
+ });
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ StringRef result_name)
+{
+ /* Use the domain of the result attribute if it already exists. */
+ std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(result_name);
+ if (meta_data) {
+ return meta_data->domain;
+ }
+
+ /* Otherwise use the highest priority domain from existing input attributes, or the default. */
+ const AttributeDomain default_domain = ATTR_DOMAIN_POINT;
+ return params.get_highest_priority_input_domain({"Vector", "Center"}, component, default_domain);
+}
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &node = params.node();
+ const NodeAttributeVectorRotate *node_storage = (const NodeAttributeVectorRotate *)node.storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (GeometryNodeAttributeVectorRotateMode)
+ node_storage->mode;
+ const std::string result_name = params.get_input<std::string>("Result");
+ const AttributeDomain result_domain = get_result_domain(component, params, result_name);
+ const bool invert = params.get_input<bool>("Invert");
+
+ GVArrayPtr attribute_vector = params.get_input_attribute(
+ "Vector", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_vector) {
+ return;
+ }
+ GVArrayPtr attribute_center = params.get_input_attribute(
+ "Center", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_center) {
+ return;
+ }
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, result_domain, CD_PROP_FLOAT3);
+ if (!attribute_result) {
+ return;
+ }
+
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ GVArrayPtr attribute_rotation = params.get_input_attribute(
+ "Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_rotation) {
+ return;
+ }
+ do_vector_rotate_euler(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ attribute_rotation->typed<float3>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ attribute_result.save();
+ return;
+ }
+
+ GVArrayPtr attribute_angle = params.get_input_attribute(
+ "Angle", component, result_domain, CD_PROP_FLOAT, nullptr);
+ if (!attribute_angle) {
+ return;
+ }
+
+ switch (mode) {
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: {
+ GVArrayPtr attribute_axis = params.get_input_attribute(
+ "Axis", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_axis) {
+ return;
+ }
+ do_vector_rotate_around_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ attribute_axis->typed<float3>(),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ } break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(1.0f, 0.0f, 0.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(0.0f, 1.0f, 0.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(0.0f, 0.0f, 1.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ:
+ /* Euler is handled before other modes to avoid processing the unavailable angle socket. */
+ BLI_assert_unreachable();
+ break;
+ }
+ attribute_result.save();
+}
+
+static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ 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>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_vector_rotate()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype,
+ GEO_NODE_ATTRIBUTE_VECTOR_ROTATE,
+ "Attribute Vector Rotate",
+ NODE_CLASS_ATTRIBUTE,
+ 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_vector_rotate_in, geo_node_attribute_vector_rotate_out);
+ node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init);
+ node_type_size(&ntype, 165, 100, 600);
+ node_type_storage(
+ &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec;
+ ntype.draw_buttons = geo_node_attribute_vector_rotate_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 9f331190420..d8029ea1eeb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -14,29 +14,29 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
-
-#include "BLI_alloca.h"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
-#include "DNA_modifier_types.h"
-#include "RNA_enum_types.h"
+#include "BKE_mesh_boolean_convert.hh"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "BKE_mesh.h"
-
-#include "bmesh.h"
-#include "tools/bmesh_boolean.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_boolean_in[] = {
{SOCK_GEOMETRY, N_("Geometry 1")},
- {SOCK_GEOMETRY, N_("Geometry 2")},
+ {SOCK_GEOMETRY,
+ N_("Geometry 2"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_MULTI_INPUT},
+ {SOCK_BOOLEAN, N_("Self Intersection")},
+ {SOCK_BOOLEAN, N_("Hole Tolerant")},
{-1, ""},
};
@@ -50,109 +50,87 @@ static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
-static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
+static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node)
{
- return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
-}
-
-static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boolean_mode)
-{
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh_a, mesh_b);
-
- BMesh *bm;
- {
- struct BMeshCreateParams bmesh_create_params = {0};
- bmesh_create_params.use_toolflags = false;
- bm = BM_mesh_create(&allocsize, &bmesh_create_params);
- }
-
- {
- struct BMeshFromMeshParams bmesh_from_mesh_params = {0};
- bmesh_from_mesh_params.calc_face_normal = true;
- BM_mesh_bm_from_me(bm, mesh_b, &bmesh_from_mesh_params);
- BM_mesh_bm_from_me(bm, mesh_a, &bmesh_from_mesh_params);
- }
-
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
- BMLoop *(*looptris)[3] = (BMLoop *
- (*)[3])(MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__));
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
-
- const int i_faces_end = mesh_b->totpoly;
-
- /* We need face normals because of 'BM_face_split_edgenet'
- * we could calculate on the fly too (before calling split). */
-
- int i = 0;
- BMIter iter;
- BMFace *bm_face;
- BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) {
- normalize_v3(bm_face->no);
+ GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)node->custom1;
- /* Temp tag to test which side split faces are from. */
- BM_elem_flag_enable(bm_face, BM_ELEM_DRAW);
+ bNodeSocket *geometry_1_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *geometry_2_socket = geometry_1_socket->next;
- i++;
- if (i == i_faces_end) {
+ switch (operation) {
+ case GEO_NODE_BOOLEAN_INTERSECT:
+ case GEO_NODE_BOOLEAN_UNION:
+ nodeSetSocketAvailability(geometry_1_socket, false);
+ nodeSetSocketAvailability(geometry_2_socket, true);
+ node_sock_label(geometry_2_socket, N_("Geometry"));
+ break;
+ case GEO_NODE_BOOLEAN_DIFFERENCE:
+ nodeSetSocketAvailability(geometry_1_socket, true);
+ nodeSetSocketAvailability(geometry_2_socket, true);
+ node_sock_label(geometry_2_socket, N_("Geometry 2"));
break;
- }
}
+}
- BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, false, boolean_mode);
-
- Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a);
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- MEM_freeN(looptris);
-
- return result;
+static void geo_node_boolean_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = GEO_NODE_BOOLEAN_DIFFERENCE;
}
namespace blender::nodes {
+
static void geo_node_boolean_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set_in_a = params.extract_input<GeometrySet>("Geometry 1");
- GeometrySet geometry_set_in_b = params.extract_input<GeometrySet>("Geometry 2");
- GeometrySet geometry_set_out;
-
GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
- if (operation < 0 || operation > 2) {
- BLI_assert(false);
- params.set_output("Geometry", std::move(geometry_set_out));
- return;
+ const bool use_self = params.get_input<bool>("Self Intersection");
+ const bool hole_tolerant = params.get_input<bool>("Hole Tolerant");
+
+#ifndef WITH_GMP
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without GMP"));
+#endif
+
+ Vector<const Mesh *> meshes;
+ Vector<const float4x4 *> transforms;
+
+ GeometrySet set_a;
+ if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
+ set_a = params.extract_input<GeometrySet>("Geometry 1");
+ /* Note that it technically wouldn't be necessary to realize the instances for the first
+ * geometry input, but the boolean code expects the first shape for the difference operation
+ * to be a single mesh. */
+ set_a = geometry_set_realize_instances(set_a);
+ const Mesh *mesh_in_a = set_a.get_mesh_for_read();
+ if (mesh_in_a != nullptr) {
+ meshes.append(mesh_in_a);
+ transforms.append(nullptr);
+ }
}
- /* TODO: Boolean does support an input of multiple meshes. Currently they must all be
- * converted to BMesh before running the operation though. D9957 will make it possible
- * to use the mesh structure directly. */
- geometry_set_in_a = geometry_set_realize_instances(geometry_set_in_a);
- geometry_set_in_b = geometry_set_realize_instances(geometry_set_in_b);
-
- const Mesh *mesh_in_a = geometry_set_in_a.get_mesh_for_read();
- const Mesh *mesh_in_b = geometry_set_in_b.get_mesh_for_read();
+ /* The instance transform matrices are owned by the instance group, so we have to
+ * keep all of them around for use during the boolean operation. */
+ Vector<bke::GeometryInstanceGroup> set_groups;
+ Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry 2");
+ for (const GeometrySet &geometry_set : geometry_sets) {
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+ }
- if (mesh_in_a == nullptr || mesh_in_b == nullptr) {
- if (operation == GEO_NODE_BOOLEAN_UNION) {
- if (mesh_in_a != nullptr) {
- params.set_output("Geometry", geometry_set_in_a);
+ for (const bke::GeometryInstanceGroup &set_group : set_groups) {
+ const Mesh *mesh_in = set_group.geometry_set.get_mesh_for_read();
+ if (mesh_in != nullptr) {
+ meshes.append_n_times(mesh_in, set_group.transforms.size());
+ for (const int i : set_group.transforms.index_range()) {
+ transforms.append(set_group.transforms.begin() + i);
}
- else {
- params.set_output("Geometry", geometry_set_in_b);
- }
- }
- else {
- params.set_output("Geometry", geometry_set_in_a);
}
- return;
}
- Mesh *mesh_out = mesh_boolean_calc(mesh_in_a, mesh_in_b, operation);
- geometry_set_out = GeometrySet::create_with_mesh(mesh_out);
+ Mesh *result = blender::meshintersect::direct_mesh_boolean(
+ meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation);
- params.set_output("Geometry", std::move(geometry_set_out));
+ params.set_output("Geometry", GeometrySet::create_with_mesh(result));
}
+
} // namespace blender::nodes
void register_node_type_geo_boolean()
@@ -162,6 +140,8 @@ void register_node_type_geo_boolean()
geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out);
ntype.draw_buttons = geo_node_boolean_layout;
+ ntype.updatefunc = geo_node_boolean_update;
+ node_type_init(&ntype, geo_node_boolean_init);
ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
new file mode 100644
index 00000000000..83d3558a7cd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -0,0 +1,177 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+#include "BKE_volume.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_bounding_box_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_bounding_box_out[] = {
+ {SOCK_GEOMETRY, N_("Bounding Box")},
+ {SOCK_VECTOR, N_("Min")},
+ {SOCK_VECTOR, N_("Max")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+using bke::GeometryInstanceGroup;
+
+static void compute_min_max_from_position_and_transform(const GeometryComponent &component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ minmax_v3v3_v3(r_min, r_max, transformed_position);
+ }
+ }
+}
+
+static void compute_min_max_from_volume_and_transforms(const VolumeComponent &volume_component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+#ifdef WITH_OPENVDB
+ const Volume *volume = volume_component.get_for_read();
+ if (volume == nullptr) {
+ return;
+ }
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+
+ for (const float4x4 &transform : transforms) {
+ openvdb::GridBase::ConstPtr instance_grid = BKE_volume_grid_shallow_transform(grid,
+ transform);
+ float3 grid_min = float3(FLT_MAX);
+ float3 grid_max = float3(-FLT_MAX);
+ if (BKE_volume_grid_bounds(instance_grid, grid_min, grid_max)) {
+ DO_MIN(grid_min, r_min);
+ DO_MAX(grid_max, r_max);
+ }
+ }
+ }
+#else
+ UNUSED_VARS(volume_component, transforms, r_min, r_max);
+#endif
+}
+
+static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve == nullptr) {
+ return;
+ }
+ for (const SplinePtr &spline : curve->splines()) {
+ Span<float3> positions = spline->evaluated_positions();
+
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ minmax_v3v3_v3(r_min, r_max, transformed_position);
+ }
+ }
+ }
+}
+
+static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set,
+ float3 &r_min,
+ float3 &r_max)
+{
+ Vector<GeometryInstanceGroup> set_groups;
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ Span<float4x4> transforms = set_group.transforms;
+
+ if (set.has<PointCloudComponent>()) {
+ compute_min_max_from_position_and_transform(
+ *set.get_component_for_read<PointCloudComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<MeshComponent>()) {
+ compute_min_max_from_position_and_transform(
+ *set.get_component_for_read<MeshComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<VolumeComponent>()) {
+ compute_min_max_from_volume_and_transforms(
+ *set.get_component_for_read<VolumeComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<CurveComponent>()) {
+ compute_min_max_from_curve_and_transforms(
+ *set.get_component_for_read<CurveComponent>(), transforms, r_min, r_max);
+ }
+ }
+}
+
+static void geo_node_bounding_box_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ float3 min = float3(FLT_MAX);
+ float3 max = float3(-FLT_MAX);
+
+ if (geometry_set.has_instances()) {
+ compute_geometry_set_instances_boundbox(geometry_set, min, max);
+ }
+ else {
+ geometry_set.compute_boundbox_without_instances(&min, &max);
+ }
+
+ if (min == float3(FLT_MAX)) {
+ params.set_output("Bounding Box", GeometrySet());
+ params.set_output("Min", float3(0));
+ params.set_output("Max", float3(0));
+ }
+ else {
+ const float3 scale = max - min;
+ const float3 center = min + scale / 2.0f;
+ Mesh *mesh = create_cube_mesh(1.0f);
+ transform_mesh(mesh, center, float3(0), scale);
+ params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Min", min);
+ params.set_output("Max", max);
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_bounding_box()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_BOUNDING_BOX, "Bounding Box", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_bounding_box_in, geo_node_bounding_box_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_bounding_box_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
index 8991a26ba4b..b2dc4661e9c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -14,8 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_matrix.h"
#include "DNA_collection_types.h"
@@ -23,8 +21,19 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_collection_info_in[] = {
- {SOCK_COLLECTION, N_("Collection")},
+ {SOCK_COLLECTION,
+ N_("Collection"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
{-1, ""},
};
@@ -40,11 +49,17 @@ static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C
namespace blender::nodes {
+static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
+ sizeof(NodeGeometryCollectionInfo), __func__);
+ data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
+ node->storage = data;
+}
+
static void geo_node_collection_info_exec(GeoNodeExecParams params)
{
- bke::PersistentCollectionHandle collection_handle =
- params.extract_input<bke::PersistentCollectionHandle>("Collection");
- Collection *collection = params.handle_map().lookup(collection_handle);
+ Collection *collection = params.get_input<Collection *>("Collection");
GeometrySet geometry_set_out;
@@ -58,10 +73,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
-
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
float transform_mat[4][4];
@@ -73,17 +84,11 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
mul_m4_m4_pre(transform_mat, self_object->imat);
}
- instances.add_instance(instance, transform_mat, -1);
- params.set_output("Geometry", geometry_set_out);
-}
+ const int handle = instances.add_reference(*collection);
+ instances.add_instance(handle, transform_mat, -1);
-static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
- sizeof(NodeGeometryCollectionInfo), __func__);
- data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
- node->storage = data;
+ params.set_output("Geometry", geometry_set_out);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc
index 441ad6bdc13..e2bb7e9f939 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_common.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc
@@ -43,3 +43,17 @@ void register_node_type_geo_group(void)
nodeRegisterType(&ntype);
}
+
+void register_node_type_geo_custom_group(bNodeType *ntype)
+{
+ /* These methods can be overridden but need a default implementation otherwise. */
+ if (ntype->poll == nullptr) {
+ ntype->poll = geo_node_poll_default;
+ }
+ if (ntype->insert_link == nullptr) {
+ ntype->insert_link = node_insert_link_default;
+ }
+ if (ntype->update_internal_links == nullptr) {
+ ntype->update_internal_links = node_update_internal_links_default;
+ }
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
new file mode 100644
index 00000000000..b1b17a321b8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -0,0 +1,321 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+#ifdef WITH_BULLET
+# include "RBI_hull_api.h"
+#endif
+
+static bNodeSocketTemplate geo_node_convex_hull_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_convex_hull_out[] = {
+ {SOCK_GEOMETRY, N_("Convex Hull")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+using bke::GeometryInstanceGroup;
+
+#ifdef WITH_BULLET
+
+static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
+{
+ plConvexHull hull = plConvexHullCompute((float(*)[3])coords.data(), coords.size());
+
+ const int num_verts = plConvexHullNumVertices(hull);
+ const int num_faces = num_verts <= 2 ? 0 : plConvexHullNumFaces(hull);
+ const int num_loops = num_verts <= 2 ? 0 : plConvexHullNumLoops(hull);
+ /* Half as many edges as loops, because the mesh is manifold. */
+ const int num_edges = num_verts == 2 ? 1 : num_verts < 2 ? 0 : num_loops / 2;
+
+ /* Create Mesh *result with proper capacity. */
+ Mesh *result;
+ if (mesh) {
+ result = BKE_mesh_new_nomain_from_template(
+ mesh, num_verts, num_edges, 0, num_loops, num_faces);
+ }
+ else {
+ result = BKE_mesh_new_nomain(num_verts, num_edges, 0, num_loops, num_faces);
+ BKE_id_material_eval_ensure_default_slot(&result->id);
+ }
+
+ /* Copy vertices. */
+ for (const int i : IndexRange(num_verts)) {
+ float co[3];
+ int original_index;
+ plConvexHullGetVertex(hull, i, co, &original_index);
+
+ if (original_index >= 0 && original_index < coords.size()) {
+# if 0 /* Disabled because it only works for meshes, not predictable enough. */
+ /* Copy custom data on vertices, like vertex groups etc. */
+ if (mesh && original_index < mesh->totvert) {
+ CustomData_copy_data(&mesh->vdata, &result->vdata, (int)original_index, (int)i, 1);
+ }
+# endif
+ /* Copy the position of the original point. */
+ copy_v3_v3(result->mvert[i].co, co);
+ }
+ else {
+ BLI_assert(!"Unexpected new vertex in hull output");
+ }
+ }
+
+ /* Copy edges and loops. */
+
+ /* NOTE: ConvexHull from Bullet uses a half-edge data structure
+ * for its mesh. To convert that, each half-edge needs to be converted
+ * to a loop and edges need to be created from that. */
+ Array<MLoop> mloop_src(num_loops);
+ uint edge_index = 0;
+ for (const int i : IndexRange(num_loops)) {
+ int v_from;
+ int v_to;
+ plConvexHullGetLoop(hull, i, &v_from, &v_to);
+
+ mloop_src[i].v = (uint)v_from;
+ /* Add edges for ascending order loops only. */
+ if (v_from < v_to) {
+ MEdge &edge = result->medge[edge_index];
+ edge.v1 = v_from;
+ edge.v2 = v_to;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+
+ /* Write edge index into both loops that have it. */
+ int reverse_index = plConvexHullGetReversedLoopIndex(hull, i);
+ mloop_src[i].e = edge_index;
+ mloop_src[reverse_index].e = edge_index;
+ edge_index++;
+ }
+ }
+ if (num_edges == 1) {
+ /* In this case there are no loops. */
+ MEdge &edge = result->medge[0];
+ edge.v1 = 0;
+ edge.v2 = 1;
+ edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE;
+ edge_index++;
+ }
+ BLI_assert(edge_index == num_edges);
+
+ /* Copy faces. */
+ Array<int> loops;
+ int j = 0;
+ MLoop *loop = result->mloop;
+ for (const int i : IndexRange(num_faces)) {
+ const int len = plConvexHullGetFaceSize(hull, i);
+
+ BLI_assert(len > 2);
+
+ /* Get face loop indices. */
+ loops.reinitialize(len);
+ plConvexHullGetFaceLoops(hull, i, loops.data());
+
+ MPoly &face = result->mpoly[i];
+ face.loopstart = j;
+ face.totloop = len;
+ for (const int k : IndexRange(len)) {
+ MLoop &src_loop = mloop_src[loops[k]];
+ loop->v = src_loop.v;
+ loop->e = src_loop.e;
+ loop++;
+ }
+ j += len;
+ }
+
+ plConvexHullDelete(hull);
+
+ BKE_mesh_calc_normals(result);
+ return result;
+}
+
+static Mesh *compute_hull(const GeometrySet &geometry_set)
+{
+ int span_count = 0;
+ int count = 0;
+ int total_size = 0;
+
+ Span<float3> positions_span;
+
+ if (geometry_set.has_mesh()) {
+ count++;
+ const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
+ total_size += component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ }
+
+ if (geometry_set.has_pointcloud()) {
+ count++;
+ span_count++;
+ const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ total_size += varray->size();
+ positions_span = varray->get_internal_span();
+ }
+
+ if (geometry_set.has_curve()) {
+ const CurveEval &curve = *geometry_set.get_curve_for_read();
+ for (const SplinePtr &spline : curve.splines()) {
+ positions_span = spline->evaluated_positions();
+ total_size += positions_span.size();
+ count++;
+ span_count++;
+ }
+ }
+
+ if (count == 0) {
+ return nullptr;
+ }
+
+ /* If there is only one positions virtual array and it is already contiguous, avoid copying
+ * all of the positions and instead pass the span directly to the convex hull function. */
+ if (span_count == 1 && count == 1) {
+ return hull_from_bullet(nullptr, positions_span);
+ }
+
+ Array<float3> positions(total_size);
+ int offset = 0;
+
+ if (geometry_set.has_mesh()) {
+ const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ varray->materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
+
+ if (geometry_set.has_pointcloud()) {
+ const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ varray->materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
+
+ if (geometry_set.has_curve()) {
+ const CurveEval &curve = *geometry_set.get_curve_for_read();
+ for (const SplinePtr &spline : curve.splines()) {
+ Span<float3> array = spline->evaluated_positions();
+ positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
+ offset += array.size();
+ }
+ }
+
+ return hull_from_bullet(geometry_set.get_mesh_for_read(), positions);
+}
+
+static void read_positions(const GeometryComponent &component,
+ Span<float4x4> transforms,
+ Vector<float3> *r_coords)
+{
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ /* NOTE: could use convex hull operation here to
+ * cut out some vertices, before accumulating,
+ * but can also be done by the user beforehand. */
+
+ r_coords->reserve(r_coords->size() + positions.size() * transforms.size());
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ r_coords->append(transformed_position);
+ }
+ }
+}
+
+static void read_curve_positions(const CurveEval &curve,
+ Span<float4x4> transforms,
+ Vector<float3> *r_coords)
+{
+ const Array<int> offsets = curve.evaluated_point_offsets();
+ const int total_size = offsets.last();
+ r_coords->reserve(r_coords->size() + total_size * transforms.size());
+ for (const SplinePtr &spline : curve.splines()) {
+ Span<float3> positions = spline->evaluated_positions();
+ for (const float4x4 &transform : transforms) {
+ for (const float3 &position : positions) {
+ r_coords->append(transform * position);
+ }
+ }
+ }
+}
+
+#endif /* WITH_BULLET */
+
+static void geo_node_convex_hull_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+#ifdef WITH_BULLET
+ Mesh *mesh = nullptr;
+ if (geometry_set.has_instances()) {
+ Vector<GeometryInstanceGroup> set_groups;
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+
+ Vector<float3> coords;
+
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ Span<float4x4> transforms = set_group.transforms;
+
+ if (set.has_pointcloud()) {
+ read_positions(*set.get_component_for_read<PointCloudComponent>(), transforms, &coords);
+ }
+ if (set.has_mesh()) {
+ read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
+ }
+ if (set.has_curve()) {
+ read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
+ }
+ }
+ mesh = hull_from_bullet(nullptr, coords);
+ }
+ else {
+ mesh = compute_hull(geometry_set);
+ }
+ params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh));
+#else
+ params.set_output("Convex Hull", geometry_set);
+#endif /* WITH_BULLET */
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_convex_hull()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CONVEX_HULL, "Convex Hull", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_convex_hull_in, geo_node_convex_hull_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_convex_hull_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
new file mode 100644
index 00000000000..306085e3b75
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_length_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_length_out[] = {
+ {SOCK_FLOAT, N_("Length")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_curve_length_exec(GeoNodeExecParams params)
+{
+ GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
+ curve_set = bke::geometry_set_realize_instances(curve_set);
+ if (!curve_set.has_curve()) {
+ params.set_output("Length", 0.0f);
+ return;
+ }
+ const CurveEval &curve = *curve_set.get_curve_for_read();
+ float length = 0.0f;
+ for (const SplinePtr &spline : curve.splines()) {
+ length += spline->length();
+ }
+ params.set_output("Length", length);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_length()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_length_in, geo_node_curve_length_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
new file mode 100644
index 00000000000..e879ec624c0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -0,0 +1,211 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_Span;
+using blender::fn::GVArray_Typed;
+
+static bNodeSocketTemplate geo_node_curve_resample_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Count"), 10, 0, 0, 0, 1, 100000},
+ {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_resample_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN(
+ sizeof(NodeGeometryCurveResample), __func__);
+
+ data->mode = GEO_NODE_CURVE_SAMPLE_COUNT;
+ node->storage = data;
+}
+
+static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+
+ bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *length_socket = count_socket->next;
+
+ nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT);
+ nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+}
+
+namespace blender::nodes {
+
+struct SampleModeParam {
+ GeometryNodeCurveSampleMode mode;
+ std::optional<float> length;
+ std::optional<int> count;
+};
+
+static SplinePtr resample_spline(const Spline &input_spline, const int count)
+{
+ std::unique_ptr<PolySpline> output_spline = std::make_unique<PolySpline>();
+ output_spline->set_cyclic(input_spline.is_cyclic());
+ output_spline->normal_mode = input_spline.normal_mode;
+
+ if (input_spline.evaluated_edges_size() < 1 || count == 1) {
+ output_spline->add_point(input_spline.positions().first(),
+ input_spline.tilts().first(),
+ input_spline.radii().first());
+ output_spline->attributes.reallocate(1);
+ return output_spline;
+ }
+
+ output_spline->resize(count);
+
+ Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count);
+
+ input_spline.sample_based_on_index_factors<float3>(
+ input_spline.evaluated_positions(), uniform_samples, output_spline->positions());
+
+ input_spline.sample_based_on_index_factors<float>(
+ input_spline.interpolate_to_evaluated_points(input_spline.radii()),
+ uniform_samples,
+ output_spline->radii());
+
+ input_spline.sample_based_on_index_factors<float>(
+ input_spline.interpolate_to_evaluated_points(input_spline.tilts()),
+ uniform_samples,
+ output_spline->tilts());
+
+ output_spline->attributes.reallocate(count);
+ input_spline.attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(name);
+ BLI_assert(input_attribute);
+ if (!output_spline->attributes.create(name, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> output_attribute = output_spline->attributes.get_for_write(
+ name);
+ if (!output_attribute) {
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ input_spline.sample_based_on_index_factors(
+ *input_spline.interpolate_to_evaluated_points(*input_attribute),
+ uniform_samples,
+ *output_attribute);
+
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ return output_spline;
+}
+
+static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
+ const SampleModeParam &mode_param)
+{
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ for (const SplinePtr &spline : input_curve.splines()) {
+ if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ BLI_assert(mode_param.count);
+ output_curve->add_spline(resample_spline(*spline, *mode_param.count));
+ }
+ else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ BLI_assert(mode_param.length);
+ const float length = spline->length();
+ const int count = std::max(int(length / *mode_param.length), 1);
+ output_curve->add_spline(resample_spline(*spline, count));
+ }
+ }
+
+ output_curve->attributes = input_curve.attributes;
+
+ return output_curve;
+}
+
+static void geo_node_resample_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_curve()) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const CurveEval &input_curve = *geometry_set.get_curve_for_read();
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ SampleModeParam mode_param;
+ mode_param.mode = mode;
+ if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ const int count = params.extract_input<int>("Count");
+ if (count < 1) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+ mode_param.count.emplace(count);
+ }
+ else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ mode_param.length.emplace(resolution);
+ }
+
+ std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
+
+ params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_resample()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_resample_in, geo_node_curve_resample_out);
+ ntype.draw_buttons = geo_node_curve_resample_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, geo_node_curve_resample_init);
+ node_type_update(&ntype, geo_node_curve_resample_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
new file mode 100644
index 00000000000..b6f04352929
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -0,0 +1,313 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_GEOMETRY, N_("Profile Curve")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void vert_extrude_to_mesh_data(const Spline &spline,
+ const float3 profile_vert,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ int &vert_offset,
+ int &edge_offset)
+{
+ Span<float3> positions = spline.evaluated_positions();
+
+ for (const int i : IndexRange(positions.size() - 1)) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset + i;
+ edge.v2 = vert_offset + i + 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset;
+ edge.v2 = vert_offset + positions.size() - 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ for (const int i : positions.index_range()) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, positions[i] + profile_vert);
+ }
+}
+
+static void mark_edges_sharp(MutableSpan<MEdge> edges)
+{
+ for (MEdge &edge : edges) {
+ edge.flag |= ME_SHARP;
+ }
+}
+
+static void spline_extrude_to_mesh_data(const Spline &spline,
+ const Spline &profile_spline,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ MutableSpan<MLoop> r_loops,
+ MutableSpan<MPoly> r_polys,
+ int &vert_offset,
+ int &edge_offset,
+ int &loop_offset,
+ int &poly_offset)
+{
+ const int spline_vert_len = spline.evaluated_points_size();
+ const int spline_edge_len = spline.evaluated_edges_size();
+ const int profile_vert_len = profile_spline.evaluated_points_size();
+ const int profile_edge_len = profile_spline.evaluated_edges_size();
+ if (spline_vert_len == 0) {
+ return;
+ }
+
+ if (profile_vert_len == 1) {
+ vert_extrude_to_mesh_data(spline,
+ profile_spline.evaluated_positions()[0],
+ r_verts,
+ r_edges,
+ vert_offset,
+ edge_offset);
+ return;
+ }
+
+ /* Add the edges running along the length of the curve, starting at each profile vertex. */
+ const int spline_edges_start = edge_offset;
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = next_ring_vert_offset + i_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Add the edges running along each profile ring. */
+ const int profile_edges_start = edge_offset;
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = ring_vert_offset + i_next_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Calculate poly and corner indices. */
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
+ const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
+ const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
+
+ MPoly &poly = r_polys[poly_offset++];
+ poly.loopstart = loop_offset;
+ poly.totloop = 4;
+ poly.flag = ME_SMOOTH;
+
+ MLoop &loop_a = r_loops[loop_offset++];
+ loop_a.v = ring_vert_offset + i_profile;
+ loop_a.e = ring_edge_start + i_profile;
+ MLoop &loop_b = r_loops[loop_offset++];
+ loop_b.v = ring_vert_offset + i_next_profile;
+ loop_b.e = next_spline_edge_start + i_ring;
+ MLoop &loop_c = r_loops[loop_offset++];
+ loop_c.v = next_ring_vert_offset + i_next_profile;
+ loop_c.e = next_ring_edge_offset + i_profile;
+ MLoop &loop_d = r_loops[loop_offset++];
+ loop_d.v = next_ring_vert_offset + i_profile;
+ loop_d.e = spline_edge_start + i_ring;
+ }
+ }
+
+ /* Calculate the positions of each profile ring profile along the spline. */
+ Span<float3> positions = spline.evaluated_positions();
+ Span<float3> tangents = spline.evaluated_tangents();
+ Span<float3> normals = spline.evaluated_normals();
+ Span<float3> profile_positions = profile_spline.evaluated_positions();
+
+ GVArray_Typed<float> radii = spline.interpolate_to_evaluated_points(spline.radii());
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ positions[i_ring], normals[i_ring], tangents[i_ring]);
+
+ point_matrix.apply_scale(radii[i_ring]);
+
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
+ }
+ }
+
+ /* Mark edge loops from sharp vector control points sharp. */
+ if (profile_spline.type() == Spline::Type::Bezier) {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline);
+ Span<int> control_point_offsets = bezier_spline.control_point_offsets();
+ for (const int i : IndexRange(bezier_spline.size())) {
+ if (bezier_spline.point_is_sharp(i)) {
+ mark_edges_sharp(r_edges.slice(
+ spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len));
+ }
+ }
+ }
+}
+
+static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve)
+{
+ int profile_vert_total = 0;
+ int profile_edge_total = 0;
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ profile_vert_total += profile_spline->evaluated_points_size();
+ profile_edge_total += profile_spline->evaluated_edges_size();
+ }
+
+ int vert_total = 0;
+ int edge_total = 0;
+ int poly_total = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ const int spline_vert_len = spline->evaluated_points_size();
+ const int spline_edge_len = spline->evaluated_edges_size();
+ vert_total += spline_vert_len * profile_vert_total;
+ poly_total += spline_edge_len * profile_edge_total;
+
+ /* Add the ring edges, with one ring for every curve vertex, and the edge loops
+ * that run along the length of the curve, starting on the first profile. */
+ edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len;
+ }
+ const int corner_total = poly_total * 4;
+
+ if (vert_total == 0) {
+ return nullptr;
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ mesh->flag |= ME_AUTOSMOOTH;
+ mesh->smoothresh = DEG2RADF(180.0f);
+
+ int vert_offset = 0;
+ int edge_offset = 0;
+ int loop_offset = 0;
+ int poly_offset = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ spline_extrude_to_mesh_data(*spline,
+ *profile_spline,
+ verts,
+ edges,
+ loops,
+ polys,
+ vert_offset,
+ edge_offset,
+ loop_offset,
+ poly_offset);
+ }
+ }
+
+ BKE_mesh_calc_normals(mesh);
+
+ return mesh;
+}
+
+static CurveEval get_curve_single_vert()
+{
+ CurveEval curve;
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->add_point(float3(0), 0, 0.0f);
+ curve.add_spline(std::move(spline));
+
+ return curve;
+}
+
+static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
+{
+ GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
+ GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
+
+ curve_set = bke::geometry_set_realize_instances(curve_set);
+ profile_set = bke::geometry_set_realize_instances(profile_set);
+
+ if (!curve_set.has_curve()) {
+ params.set_output("Mesh", GeometrySet());
+ return;
+ }
+
+ const CurveEval *profile_curve = profile_set.get_curve_for_read();
+
+ static const CurveEval vert_curve = get_curve_single_vert();
+
+ Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(),
+ (profile_curve == nullptr) ? vert_curve : *profile_curve);
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_to_mesh()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
new file mode 100644
index 00000000000..910adc467d6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -0,0 +1,678 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+using blender::bke::CustomDataAttributes;
+
+/* Code from the mask modifier in MOD_mask.cc. */
+extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map);
+extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map,
+ blender::Span<int> edge_map);
+extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map,
+ blender::Span<int> edge_map,
+ blender::Span<int> masked_poly_indices,
+ blender::Span<int> new_loop_starts);
+
+static bNodeSocketTemplate geo_node_delete_geometry_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Selection")},
+ {SOCK_BOOLEAN, N_("Invert")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_delete_geometry_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
+{
+ for (const int i_out : mask.index_range()) {
+ r_data[i_out] = data[mask[i_out]];
+ }
+}
+
+static void spline_copy_builtin_attributes(const Spline &spline,
+ Spline &r_spline,
+ const IndexMask mask)
+{
+ copy_data(spline.positions(), r_spline.positions(), mask);
+ copy_data(spline.radii(), r_spline.radii(), mask);
+ copy_data(spline.tilts(), r_spline.tilts(), mask);
+ switch (spline.type()) {
+ case Spline::Type::Poly:
+ break;
+ case Spline::Type::Bezier: {
+ const BezierSpline &src = static_cast<const BezierSpline &>(spline);
+ BezierSpline &dst = static_cast<BezierSpline &>(r_spline);
+ copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask);
+ copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask);
+ copy_data(src.handle_types_left(), dst.handle_types_left(), mask);
+ copy_data(src.handle_types_right(), dst.handle_types_right(), mask);
+ break;
+ }
+ case Spline::Type::NURBS: {
+ const NURBSpline &src = static_cast<const NURBSpline &>(spline);
+ NURBSpline &dst = static_cast<NURBSpline &>(r_spline);
+ copy_data(src.weights(), dst.weights(), mask);
+ break;
+ }
+ }
+}
+
+static void copy_dynamic_attributes(const CustomDataAttributes &src,
+ CustomDataAttributes &dst,
+ const IndexMask mask)
+{
+ src.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src_attribute = src.get_for_read(name);
+ BLI_assert(src_attribute);
+
+ if (!dst.create(name, meta_data.data_type)) {
+ /* Since the source spline of the same type had the attribute, adding it should work. */
+ BLI_assert_unreachable();
+ }
+
+ std::optional<GMutableSpan> new_attribute = dst.get_for_write(name);
+ BLI_assert(new_attribute);
+
+ attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_data(src_attribute->typed<T>(), new_attribute->typed<T>(), mask);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr spline_delete(const Spline &spline, const IndexMask mask)
+{
+ SplinePtr new_spline = spline.copy_settings();
+ new_spline->resize(mask.size());
+
+ spline_copy_builtin_attributes(spline, *new_spline, mask);
+ copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask);
+
+ return new_spline;
+}
+
+static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
+ const StringRef name,
+ const bool invert)
+{
+ Span<SplinePtr> input_splines = input_curve.splines();
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ /* Keep track of which splines were copied to the result to copy spline domain attributes. */
+ Vector<int64_t> copied_splines;
+
+ if (input_curve.attributes.get_for_read(name)) {
+ GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false);
+ for (const int i : input_splines.index_range()) {
+ if (selection[i] == invert) {
+ output_curve->add_spline(input_splines[i]->copy());
+ copied_splines.append(i);
+ }
+ }
+ }
+ else {
+ /* Reuse index vector for each spline. */
+ Vector<int64_t> indices_to_copy;
+
+ for (const int i : input_splines.index_range()) {
+ const Spline &spline = *input_splines[i];
+ GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false);
+
+ indices_to_copy.clear();
+ for (const int i_point : IndexRange(spline.size())) {
+ if (selection[i_point] == invert) {
+ indices_to_copy.append(i_point);
+ }
+ }
+
+ /* Avoid creating an empty spline. */
+ if (indices_to_copy.is_empty()) {
+ continue;
+ }
+
+ SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy));
+ output_curve->add_spline(std::move(new_spline));
+ copied_splines.append(i);
+ }
+ }
+
+ if (copied_splines.is_empty()) {
+ return {};
+ }
+
+ output_curve->attributes.reallocate(output_curve->splines().size());
+ copy_dynamic_attributes(
+ input_curve.attributes, output_curve->attributes, IndexMask(copied_splines));
+
+ return output_curve;
+}
+
+static void delete_curve_selection(const CurveComponent &in_component,
+ CurveComponent &r_component,
+ const StringRef selection_name,
+ const bool invert)
+{
+ std::unique_ptr<CurveEval> r_curve = curve_delete(
+ *in_component.get_for_read(), selection_name, invert);
+ if (r_curve) {
+ r_component.replace(r_curve.release());
+ }
+ else {
+ r_component.clear();
+ }
+}
+
+static void delete_point_cloud_selection(const PointCloudComponent &in_component,
+ PointCloudComponent &out_component,
+ const StringRef selection_name,
+ const bool invert)
+{
+ const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>(
+ selection_name, ATTR_DOMAIN_POINT, false);
+ VArray_Span<bool> selection{selection_attribute};
+
+ const int total = selection.count(invert);
+ if (total == 0) {
+ out_component.clear();
+ return;
+ }
+ out_component.replace(BKE_pointcloud_new_nomain(total));
+
+ /* Invert the inversion, because this deletes the selected points instead of keeping them. */
+ copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert);
+}
+
+static void compute_selected_vertices_from_vertex_selection(const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ uint *r_num_selected_vertices)
+{
+ BLI_assert(vertex_selection.size() == r_vertex_map.size());
+
+ uint num_selected_vertices = 0;
+ for (const int i : r_vertex_map.index_range()) {
+ if (vertex_selection[i] != invert) {
+ r_vertex_map[i] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ else {
+ r_vertex_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_vertices = num_selected_vertices;
+}
+
+static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_edge_map,
+ uint *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == r_edge_map.size());
+
+ uint num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+
+ /* Only add the edge if both vertices will be in the new mesh. */
+ if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_edges = num_selected_edges;
+}
+
+static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ BLI_assert(mesh.totvert == vertex_selection.size());
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+
+ bool all_verts_in_selection = true;
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ if (vertex_selection[loop.v] == invert) {
+ all_verts_in_selection = false;
+ break;
+ }
+ }
+
+ if (all_verts_in_selection) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the edge
+ * are kept along with the edge.
+ */
+static void compute_selected_vertices_and_edges_from_edge_selection(
+ const Mesh &mesh,
+ const VArray<bool> &edge_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == edge_selection.size());
+
+ uint num_selected_edges = 0;
+ uint num_selected_vertices = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+ if (edge_selection[i] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ if (r_vertex_map[edge.v1] == -1) {
+ r_vertex_map[edge.v1] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ if (r_vertex_map[edge.v2] == -1) {
+ r_vertex_map[edge.v2] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_vertices = num_selected_vertices;
+ *r_num_selected_edges = num_selected_edges;
+}
+
+/**
+ * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that
+ * polygon is kept.
+ */
+static void compute_selected_polygons_from_edge_selection(const Mesh &mesh,
+ const VArray<bool> &edge_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+
+ bool all_edges_in_selection = true;
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ if (edge_selection[loop.e] == invert) {
+ all_edges_in_selection = false;
+ break;
+ }
+ }
+
+ if (all_edges_in_selection) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all
+ * vertices of that polygon or edge are in the selection.
+ */
+static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ compute_selected_vertices_from_vertex_selection(
+ vertex_selection, invert, r_vertex_map, r_num_selected_vertices);
+
+ compute_selected_edges_from_vertex_selection(
+ mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges);
+
+ compute_selected_polygons_from_vertex_selection(mesh,
+ vertex_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to
+ * that edge are kept as well. The polygons are kept if all edges are in the selection.
+ */
+static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
+ const VArray<bool> &edge_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ r_vertex_map.fill(-1);
+ compute_selected_vertices_and_edges_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_vertex_map,
+ r_edge_map,
+ r_num_selected_vertices,
+ r_num_selected_edges);
+ compute_selected_polygons_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices
+ * belonging to that polygon are kept as well.
+ */
+static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
+ const VArray<bool> &poly_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ BLI_assert(mesh.totpoly == poly_selection.size());
+ BLI_assert(mesh.totedge == r_edge_map.size());
+ r_vertex_map.fill(-1);
+ r_edge_map.fill(-1);
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ uint num_selected_vertices = 0;
+ uint num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+ /* We keep this one. */
+ if (poly_selection[i] != invert) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+
+ /* Add the vertices and the edges. */
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ /* Check first if it has not yet been added. */
+ if (r_vertex_map[loop.v] == -1) {
+ r_vertex_map[loop.v] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ if (r_edge_map[loop.e] == -1) {
+ r_edge_map[loop.e] = num_selected_edges;
+ num_selected_edges++;
+ }
+ }
+ }
+ }
+ *r_num_selected_vertices = num_selected_vertices;
+ *r_num_selected_edges = num_selected_edges;
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+using FillMapsFunction = void (*)(const Mesh &mesh,
+ const VArray<bool> &selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops);
+
+/**
+ * Delete the parts of the mesh that are in the selection. The `fill_maps_function`
+ * depends on the selection type: vertices, edges or faces.
+ */
+static Mesh *delete_mesh_selection(const Mesh &mesh_in,
+ const VArray<bool> &selection,
+ const bool invert,
+ FillMapsFunction fill_maps_function)
+{
+ Array<int> vertex_map(mesh_in.totvert);
+ uint num_selected_vertices;
+
+ Array<int> edge_map(mesh_in.totedge);
+ uint num_selected_edges;
+
+ Vector<int> selected_poly_indices;
+ Vector<int> new_loop_starts;
+ uint num_selected_polys;
+ uint num_selected_loops;
+
+ /* Fill all the maps based on the selection. We delete everything
+ * in the selection instead of keeping it, so we need to invert it. */
+ fill_maps_function(mesh_in,
+ selection,
+ !invert,
+ vertex_map,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_vertices,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+
+ Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in,
+ num_selected_vertices,
+ num_selected_edges,
+ 0,
+ num_selected_loops,
+ num_selected_polys);
+
+ /* Copy the selected parts of the mesh over to the new mesh. */
+ copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map);
+ copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map);
+ copy_masked_polys_to_new_mesh(
+ mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
+ BKE_mesh_calc_edges_loose(result);
+ /* Tag to recalculate normals later. */
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+
+ return result;
+}
+
+static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name)
+{
+ std::optional<AttributeMetaData> selection_attribute = component.attribute_get_meta_data(name);
+ if (!selection_attribute) {
+ /* The node will not do anything in this case, but this function must return something. */
+ return ATTR_DOMAIN_POINT;
+ }
+
+ /* Corners can't be deleted separately, so interpolate corner attributes
+ * to the face domain. Note that this choice is somewhat arbitrary. */
+ if (selection_attribute->domain == ATTR_DOMAIN_CORNER) {
+ return ATTR_DOMAIN_FACE;
+ }
+
+ return selection_attribute->domain;
+}
+
+static void delete_mesh_selection(MeshComponent &component,
+ const Mesh &mesh_in,
+ const StringRef selection_name,
+ const bool invert)
+{
+ /* Figure out the best domain to use. */
+ const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name);
+
+ /* This already checks if the attribute exists, and displays a warning in that case. */
+ GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
+ selection_name, selection_domain, false);
+
+ /* Check if there is anything to delete. */
+ bool delete_nothing = true;
+ for (const int i : selection.index_range()) {
+ if (selection[i] != invert) {
+ delete_nothing = false;
+ break;
+ }
+ }
+ if (delete_nothing) {
+ return;
+ }
+
+ Mesh *mesh_out;
+ switch (selection_domain) {
+ case ATTR_DOMAIN_POINT:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection);
+ break;
+ case ATTR_DOMAIN_FACE:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection);
+ break;
+ default:
+ BLI_assert_unreachable();
+ mesh_out = nullptr;
+ break;
+ }
+ component.replace_mesh_but_keep_vertex_group_names(mesh_out);
+}
+
+static void geo_node_delete_geometry_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ const bool invert = params.extract_input<bool>("Invert");
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ if (selection_name.empty()) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ GeometrySet out_set(geometry_set);
+ if (geometry_set.has<PointCloudComponent>()) {
+ delete_point_cloud_selection(*geometry_set.get_component_for_read<PointCloudComponent>(),
+ out_set.get_component_for_write<PointCloudComponent>(),
+ selection_name,
+ invert);
+ }
+ if (geometry_set.has<MeshComponent>()) {
+ delete_mesh_selection(out_set.get_component_for_write<MeshComponent>(),
+ *geometry_set.get_mesh_for_read(),
+ selection_name,
+ invert);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ delete_curve_selection(*geometry_set.get_component_for_read<CurveComponent>(),
+ out_set.get_component_for_write<CurveComponent>(),
+ selection_name,
+ invert);
+ }
+
+ params.set_output("Geometry", std::move(out_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_delete_geometry()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
index 1c794d1f43e..740b828d503 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -14,9 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_math_base.h"
-#include "BLI_math_rotation.h"
-
#include "DNA_modifier_types.h"
#include "node_geometry_util.hh"
@@ -47,6 +44,7 @@ static bNodeSocketTemplate geo_node_edge_split_out[] = {
};
namespace blender::nodes {
+
static void geo_node_edge_split_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -85,6 +83,7 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_edge_split()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
new file mode 100644
index 00000000000..6bad71a62a2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
@@ -0,0 +1,51 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_input_material_out[] = {
+ {SOCK_MATERIAL, N_("Material")},
+ {-1, ""},
+};
+
+static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "material", 0, "", ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_input_material_exec(GeoNodeExecParams params)
+{
+ Material *material = (Material *)params.node().id;
+ params.set_output("Material", material);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_input_material()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, nullptr, geo_node_input_material_out);
+ ntype.draw_buttons = geo_node_input_material_layout;
+ ntype.geometry_node_execute = blender::nodes::geo_node_input_material_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
index 667beaa1722..ec875b9f983 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
@@ -14,10 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "DEG_depsgraph_query.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_is_viewport_out[] = {
{SOCK_BOOLEAN, N_("Is Viewport")},
{-1, ""},
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 52512769a47..adfd924f185 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -14,9 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -56,6 +58,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
+ VectorSet<Material *> materials;
+
for (const MeshComponent *mesh_component : src_components) {
const Mesh *mesh = mesh_component->get_for_read();
totverts += mesh->totvert;
@@ -66,12 +70,22 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
cd_dirty_poly |= mesh->runtime.cd_dirty_poly;
cd_dirty_edge |= mesh->runtime.cd_dirty_edge;
cd_dirty_loop |= mesh->runtime.cd_dirty_loop;
+
+ for (const int slot_index : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[slot_index];
+ materials.add(material);
+ }
}
const Mesh *first_input_mesh = src_components[0]->get_for_read();
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
BKE_mesh_copy_settings(new_mesh, first_input_mesh);
+ for (const int i : IndexRange(materials.size())) {
+ Material *material = materials[i];
+ BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
+ }
+
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@@ -87,6 +101,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
continue;
}
+ Array<int> material_index_map(mesh->totcol);
+ for (const int i : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[i];
+ const int new_material_index = materials.index_of(material);
+ material_index_map[i] = new_material_index;
+ }
+
for (const int i : IndexRange(mesh->totvert)) {
const MVert &old_vert = mesh->mvert[i];
MVert &new_vert = new_mesh->mvert[vert_offset + i];
@@ -112,6 +133,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
+ if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh->totcol) {
+ new_poly.mat_nr = material_index_map[new_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ new_poly.mat_nr = 0;
+ }
}
vert_offset += mesh->totvert;
@@ -149,10 +177,10 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *>
Vector<CustomDataType> data_types;
Vector<AttributeDomain> domains;
for (const GeometryComponent *component : components) {
- ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
+ ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name);
if (attribute) {
- data_types.append(attribute->custom_data_type());
- domains.append(attribute->domain());
+ data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
+ domains.append(attribute.domain);
}
}
@@ -164,7 +192,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
StringRef attribute_name,
const CustomDataType data_type,
const AttributeDomain domain,
- fn::GMutableSpan dst_span)
+ GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
@@ -175,10 +203,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
if (domain_size == 0) {
continue;
}
- ReadAttributePtr read_attribute = component->attribute_get_for_read(
+ GVArrayPtr read_attribute = component->attribute_get_for_read(
attribute_name, domain, data_type, nullptr);
- fn::GSpan src_span = read_attribute->get_span();
+ GVArray_GSpan src_span{*read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
@@ -201,16 +229,14 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
AttributeDomain domain;
determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
- OutputAttributePtr write_attribute = result.attribute_try_get_for_output(
+ OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
attribute_name, domain, data_type);
- if (!write_attribute ||
- &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
- write_attribute->domain() != domain) {
+ if (!write_attribute) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+ GMutableSpan dst_span = write_attribute.as_span();
fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
- write_attribute.apply_span_and_save();
+ write_attribute.save();
}
}
@@ -244,12 +270,30 @@ static void join_components(Span<const PointCloudComponent *> src_components, Ge
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
- for (const InstancesComponent *component : src_components) {
- const int size = component->instances_amount();
- Span<InstancedData> instanced_data = component->instanced_data();
- Span<float4x4> transforms = component->transforms();
- for (const int i : IndexRange(size)) {
- dst_component.add_instance(instanced_data[i], transforms[i]);
+
+ int tot_instances = 0;
+ for (const InstancesComponent *src_component : src_components) {
+ tot_instances += src_component->instances_amount();
+ }
+ dst_component.reserve(tot_instances);
+
+ for (const InstancesComponent *src_component : src_components) {
+ Span<InstanceReference> src_references = src_component->references();
+ Array<int> handle_map(src_references.size());
+ for (const int src_handle : src_references.index_range()) {
+ handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]);
+ }
+
+ Span<float4x4> src_transforms = src_component->instance_transforms();
+ Span<int> src_ids = src_component->instance_ids();
+ Span<int> src_reference_handles = src_component->instance_reference_handles();
+
+ for (const int i : src_transforms.index_range()) {
+ const int src_handle = src_reference_handles[i];
+ const int dst_handle = handle_map[src_handle];
+ const float4x4 &transform = src_transforms[i];
+ const int id = src_ids[i];
+ dst_component.add_instance(dst_handle, transform, id);
}
}
}
@@ -262,6 +306,48 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
+static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
+{
+ Vector<CurveComponent *> src_components;
+ for (GeometrySet &geometry_set : src_geometry_sets) {
+ if (geometry_set.has_curve()) {
+ /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy
+ * in the case where the input spline has no other users, because the splines can be
+ * moved from the source curve rather than copied from a read-only source. Retrieving
+ * the curve for write will make a copy only when it has a user elsewhere. */
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ src_components.append(&component);
+ }
+ }
+
+ if (src_components.size() == 0) {
+ return;
+ }
+ if (src_components.size() == 1) {
+ result.add(*src_components[0]);
+ return;
+ }
+
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ CurveEval *dst_curve = new CurveEval();
+ for (CurveComponent *component : src_components) {
+ CurveEval *src_curve = component->get_for_write();
+ for (SplinePtr &spline : src_curve->splines()) {
+ dst_curve->add_spline(std::move(spline));
+ }
+ }
+
+ /* For now, remove all custom attributes, since they might have different types,
+ * or an attribute might not exist on all splines. */
+ dst_curve->attributes.reallocate(dst_curve->splines().size());
+ CustomData_reset(&dst_curve->attributes.data);
+ for (SplinePtr &spline : dst_curve->splines()) {
+ CustomData_reset(&spline->attributes.data);
+ }
+
+ dst_component.replace(dst_curve);
+}
+
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
{
@@ -292,6 +378,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
+ join_curve_components(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
new file mode 100644
index 00000000000..02b2d685bdd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_assign_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL,
+ N_("Material"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_assign_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material)
+{
+ int new_material_index = -1;
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *other_material = mesh.mat[i];
+ if (other_material == material) {
+ new_material_index = i;
+ break;
+ }
+ }
+ if (new_material_index == -1) {
+ /* Append a new material index. */
+ new_material_index = mesh.totcol;
+ BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material);
+ }
+
+ mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly);
+ for (const int i : IndexRange(mesh.totpoly)) {
+ if (face_mask[i]) {
+ MPoly &poly = mesh.mpoly[i];
+ poly.mat_nr = new_material_index;
+ }
+ }
+}
+
+static void geo_node_material_assign_exec(GeoNodeExecParams params)
+{
+ Material *material = params.extract_input<Material *>("Material");
+ const std::string mask_name = params.extract_input<std::string>("Selection");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
+ mask_name, ATTR_DOMAIN_FACE, true);
+ assign_material_to_faces(*mesh, face_mask, material);
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_material_assign()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_material_assign_in, geo_node_material_assign_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
new file mode 100644
index 00000000000..40ecab98dea
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
@@ -0,0 +1,75 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_replace_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL, N_("Old")},
+ {SOCK_MATERIAL, N_("New")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_replace_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_material_replace_exec(GeoNodeExecParams params)
+{
+ Material *old_material = params.extract_input<Material *>("Old");
+ Material *new_material = params.extract_input<Material *>("New");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ for (const int i : IndexRange(mesh->totcol)) {
+ if (mesh->mat[i] == old_material) {
+ mesh->mat[i] = new_material;
+ }
+ }
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_material_replace()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_material_replace_in, geo_node_material_replace_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
index 01016ec9b44..2915a17d2c8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -119,6 +120,7 @@ static Mesh *create_circle_mesh(const float radius,
0,
circle_corner_total(fill_type, verts_num),
circle_face_total(fill_type, verts_num));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
@@ -161,6 +163,7 @@ static Mesh *create_circle_mesh(const float radius,
MEdge &edge = edges[verts_num + i];
edge.v1 = verts_num;
edge.v2 = i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index e9228a2942b..925ed0f8da8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
@@ -17,14 +17,12 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "bmesh.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = {
@@ -189,45 +187,358 @@ static int face_total(const GeometryNodeMeshCircleFillType fill_type,
return face_total;
}
+static void calculate_uvs(Mesh *mesh,
+ const bool top_is_point,
+ const bool bottom_is_point,
+ const int verts_num,
+ const GeometryNodeMeshCircleFillType fill_type)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ Array<float2> circle(verts_num);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
+ for (const int i : IndexRange(verts_num)) {
+ circle[i].x = std::cos(angle) * 0.225f + 0.25f;
+ circle[i].y = std::sin(angle) * 0.225f + 0.25f;
+ angle += angle_delta;
+ }
+
+ int loop_index = 0;
+ if (!top_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ uvs[loop_index++] = circle[(i + 1) % verts_num];
+ uvs[loop_index++] = float2(0.25f, 0.25f);
+ }
+ }
+ }
+
+ /* Create side corners and faces. */
+ if (!top_is_point && !bottom_is_point) {
+ const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
+ /* Quads connect the top and bottom. */
+ for (const int i : IndexRange(verts_num)) {
+ const float vert = static_cast<float>(i);
+ uvs[loop_index++] = float2(vert / verts_num, bottom);
+ uvs[loop_index++] = float2(vert / verts_num, 1.0f);
+ uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f);
+ uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom);
+ }
+ }
+ else {
+ /* Triangles connect the top and bottom section. */
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
+ uvs[loop_index++] = float2(0.75f, 0.25f);
+ uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+ }
+ else {
+ BLI_assert(!bottom_is_point);
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ uvs[loop_index++] = circle[(i + 1) % verts_num];
+ uvs[loop_index++] = float2(0.25f, 0.25f);
+ }
+ }
+ }
+
+ /* Create bottom corners and faces. */
+ if (!bottom_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ for (const int i : IndexRange(verts_num)) {
+ /* Go backwards because of reversed face normal. */
+ uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f);
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
+ uvs[loop_index++] = float2(0.75f, 0.25f);
+ uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+ }
+ }
+
+ uv_attribute.save();
+}
+
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
const float radius_bottom,
const float depth,
const int verts_num,
const GeometryNodeMeshCircleFillType fill_type)
{
- const float4x4 transform = float4x4::identity();
-
const bool top_is_point = radius_top == 0.0f;
const bool bottom_is_point = radius_bottom == 0.0f;
+ const float height = depth * 0.5f;
+ /* Handle the case of a line / single point before everything else to avoid
+ * the need to check for it later. */
+ if (top_is_point && bottom_is_point) {
+ const bool single_vertex = height == 0.0f;
+ Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0);
+ copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height));
+ if (single_vertex) {
+ const short up[3] = {0, 0, SHRT_MAX};
+ copy_v3_v3_short(mesh->mvert[0].no, up);
+ return mesh;
+ }
+ copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height));
+ mesh->medge[0].v1 = 0;
+ mesh->medge[0].v2 = 1;
+ mesh->medge[0].flag |= ME_LOOSEEDGE;
+ BKE_mesh_calc_normals(mesh);
+ return mesh;
+ }
- const BMeshCreateParams bmcp = {true};
- const BMAllocTemplate allocsize = {
+ Mesh *mesh = BKE_mesh_new_nomain(
vert_total(fill_type, verts_num, top_is_point, bottom_is_point),
edge_total(fill_type, verts_num, top_is_point, bottom_is_point),
+ 0,
corner_total(fill_type, verts_num, top_is_point, bottom_is_point),
- face_total(fill_type, verts_num, top_is_point, bottom_is_point)};
- BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
-
- const bool cap_end = (fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE);
- const bool cap_tri = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN);
- BMO_op_callf(bm,
- BMO_FLAG_DEFAULTS,
- "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b "
- "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
- verts_num,
- radius_bottom,
- radius_top,
- cap_end,
- cap_tri,
- depth,
- transform.values,
- true);
-
- BMeshToMeshParams params{};
- params.calc_object_remap = false;
- Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
- BM_mesh_free(bm);
+ face_total(fill_type, verts_num, top_is_point, bottom_is_point));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ /* Calculate vertex positions. */
+ const int top_verts_start = 0;
+ const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
+ for (const int i : IndexRange(verts_num)) {
+ const float x = std::cos(angle);
+ const float y = std::sin(angle);
+ if (!top_is_point) {
+ copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height));
+ }
+ if (!bottom_is_point) {
+ copy_v3_v3(verts[bottom_verts_start + i].co,
+ float3(x * radius_bottom, y * radius_bottom, -height));
+ }
+ angle += angle_delta;
+ }
+ if (top_is_point) {
+ copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height));
+ }
+ if (bottom_is_point) {
+ copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height));
+ }
+
+ /* Add center vertices for the triangle fans at the end. */
+ const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num);
+ const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1);
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ if (!top_is_point) {
+ copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height));
+ }
+ if (!bottom_is_point) {
+ copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height));
+ }
+ }
+
+ /* Create top edges. */
+ const int top_edges_start = 0;
+ const int top_fan_edges_start = (!top_is_point &&
+ fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
+ top_edges_start + verts_num :
+ top_edges_start;
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[top_edges_start + i];
+ edge.v1 = top_verts_start + i;
+ edge.v2 = top_verts_start + (i + 1) % verts_num;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[top_fan_edges_start + i];
+ edge.v1 = top_center_vert_index;
+ edge.v2 = top_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ }
+
+ /* Create connecting edges. */
+ const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0);
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[connecting_edges_start + i];
+ edge.v1 = top_verts_start + (!top_is_point ? i : 0);
+ edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ /* Create bottom edges. */
+ const int bottom_edges_start = connecting_edges_start + verts_num;
+ const int bottom_fan_edges_start = (!bottom_is_point &&
+ fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
+ bottom_edges_start + verts_num :
+ bottom_edges_start;
+ if (!bottom_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[bottom_edges_start + i];
+ edge.v1 = bottom_verts_start + i;
+ edge.v2 = bottom_verts_start + (i + 1) % verts_num;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[bottom_fan_edges_start + i];
+ edge.v1 = bottom_center_vert_index;
+ edge.v2 = bottom_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ }
+
+ /* Create top corners and faces. */
+ int loop_index = 0;
+ int poly_index = 0;
+ if (!top_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = verts_num;
+
+ for (const int i : IndexRange(verts_num)) {
+ MLoop &loop = loops[loop_index++];
+ loop.v = top_verts_start + i;
+ loop.e = top_edges_start + i;
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = top_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = top_verts_start + (i + 1) % verts_num;
+ loop_b.e = top_fan_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_center_vert_index;
+ loop_c.e = top_fan_edges_start + i;
+ }
+ }
+ }
+
+ /* Create side corners and faces. */
+ if (!top_is_point && !bottom_is_point) {
+ /* Quads connect the top and bottom. */
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = connecting_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start + i;
+ loop_b.e = bottom_edges_start + i;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_c.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = top_verts_start + (i + 1) % verts_num;
+ loop_d.e = top_edges_start + i;
+ }
+ }
+ else {
+ /* Triangles connect the top and bottom section. */
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = connecting_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start;
+ loop_b.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_verts_start + (i + 1) % verts_num;
+ loop_c.e = top_edges_start + i;
+ }
+ }
+ else {
+ BLI_assert(!bottom_is_point);
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = bottom_verts_start + i;
+ loop_a.e = bottom_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_b.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_verts_start;
+ loop_c.e = connecting_edges_start + i;
+ }
+ }
+ }
+
+ /* Create bottom corners and faces. */
+ if (!bottom_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = verts_num;
+
+ for (const int i : IndexRange(verts_num)) {
+ /* Go backwards to reverse surface normal. */
+ MLoop &loop = loops[loop_index++];
+ loop.v = bottom_verts_start + verts_num - 1 - i;
+ loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num;
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = bottom_verts_start + i;
+ loop_a.e = bottom_fan_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_center_vert_index;
+ loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_c.e = bottom_edges_start + i;
+ }
+ }
+ }
+
+ BKE_mesh_calc_normals(mesh);
+
+ calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type);
+
+ BLI_assert(BKE_mesh_is_valid(mesh));
return mesh;
}
@@ -253,6 +564,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
Mesh *mesh = create_cylinder_or_cone_mesh(
radius_top, radius_bottom, depth, verts_num, fill_type);
+ /* Transform the mesh so that the base of the cone is at the origin. */
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
index f8a9bfd2ed1..9651301cb34 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "bmesh.h"
@@ -35,7 +36,7 @@ static bNodeSocketTemplate geo_node_mesh_primitive_cube_out[] = {
namespace blender::nodes {
-static Mesh *create_cube_mesh(const float size)
+Mesh *create_cube_mesh(const float size)
{
const float4x4 transform = float4x4::identity();
@@ -53,6 +54,7 @@ static Mesh *create_cube_mesh(const float size)
BMeshToMeshParams params{};
params.calc_object_remap = false;
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
BM_mesh_free(bm);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
index f443b4387d3..1767f765da4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index eff84d7d1ad..ac2f5a23a4d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -14,12 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_map.hh"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -27,39 +25,47 @@
#include "node_geometry_util.hh"
-static bNodeSocketTemplate geo_node_mesh_primitive_plane_in[] = {
- {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
- {SOCK_INT, N_("Vertices X"), 10, 0.0f, 0.0f, 0.0f, 2, 1000},
- {SOCK_INT, N_("Vertices Y"), 10, 0.0f, 0.0f, 0.0f, 2, 1000},
+static bNodeSocketTemplate geo_node_mesh_primitive_grid_in[] = {
+ {SOCK_FLOAT, N_("Size X"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
+ {SOCK_FLOAT, N_("Size Y"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
+ {SOCK_INT, N_("Vertices X"), 3, 0.0f, 0.0f, 0.0f, 2, 1000},
+ {SOCK_INT, N_("Vertices Y"), 3, 0.0f, 0.0f, 0.0f, 2, 1000},
{-1, ""},
};
-static bNodeSocketTemplate geo_node_mesh_primitive_plane_out[] = {
+static bNodeSocketTemplate geo_node_mesh_primitive_grid_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
-static void calculate_uvs(Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size)
+static void calculate_uvs(
+ Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+ const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
+ const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
for (const int i : loops.index_range()) {
const float3 &co = verts[loops[i].v].co;
- uvs[i].x = (co.x + size) / (size * 2.0f);
- uvs[i].y = (co.y + size) / (size * 2.0f);
+ uvs[i].x = (co.x + size_x * 0.5f) * dx;
+ uvs[i].y = (co.y + size_y * 0.5f) * dy;
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
-static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float size)
+static Mesh *create_grid_mesh(const int verts_x,
+ const int verts_y,
+ const float size_x,
+ const float size_y)
{
+ BLI_assert(verts_x > 1 && verts_y > 1);
const int edges_x = verts_x - 1;
const int edges_y = verts_y - 1;
Mesh *mesh = BKE_mesh_new_nomain(verts_x * verts_y,
@@ -73,11 +79,11 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
{
- const float dx = size / edges_x;
- const float dy = size / edges_y;
- float x = -size;
+ const float dx = size_x / edges_x;
+ const float dy = size_y / edges_y;
+ float x = -size_x * 0.5;
for (const int x_index : IndexRange(verts_x)) {
- float y = -size;
+ float y = -size_y * 0.5;
for (const int y_index : IndexRange(verts_y)) {
const int vert_index = x_index * verts_y + y_index;
verts[vert_index].co[0] = x;
@@ -104,6 +110,7 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MEdge &edge = edges[edge_index++];
edge.v1 = vert_index;
edge.v2 = vert_index + 1;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
@@ -115,6 +122,7 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MEdge &edge = edges[edge_index++];
edge.v1 = vert_index;
edge.v2 = vert_index + verts_y;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
@@ -142,14 +150,15 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
}
}
- calculate_uvs(mesh, verts, loops, size);
+ calculate_uvs(mesh, verts, loops, size_x, size_y);
return mesh;
}
-static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params)
+static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params)
{
- const float size = params.extract_input<float>("Size");
+ const float size_x = params.extract_input<float>("Size X");
+ const float size_y = params.extract_input<float>("Size Y");
const int verts_x = params.extract_input<int>("Vertices X");
const int verts_y = params.extract_input<int>("Vertices Y");
if (verts_x < 2 || verts_y < 2) {
@@ -157,21 +166,22 @@ static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params)
return;
}
- Mesh *mesh = create_plane_mesh(verts_x, verts_y, size);
+ Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y);
BLI_assert(BKE_mesh_is_valid(mesh));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
-void register_node_type_geo_mesh_primitive_plane()
+void register_node_type_geo_mesh_primitive_grid()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_PLANE, "Plane", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_GRID, "Grid", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(
- &ntype, geo_node_mesh_primitive_plane_in, geo_node_mesh_primitive_plane_out);
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_plane_exec;
+ &ntype, geo_node_mesh_primitive_grid_in, geo_node_mesh_primitive_grid_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_grid_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
index 242cc6ed7df..a3a1b72006c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "bmesh.h"
@@ -55,6 +56,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius)
BMeshToMeshParams params{};
params.calc_object_remap = false;
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
BM_mesh_free(bm);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
index a7d40571c39..e841455e58c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
@@ -14,12 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_map.hh"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -113,6 +111,7 @@ static Mesh *create_line_mesh(const float3 start, const float3 delta, const int
}
Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
@@ -154,7 +153,10 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
}
else if (count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL) {
const int count = params.extract_input<int>("Count");
- if (count > 1) {
+ if (count == 1) {
+ mesh = create_line_mesh(start, float3(0), count);
+ }
+ else {
const float3 delta = total_delta / (float)(count - 1);
mesh = create_line_mesh(start, delta, count);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
index 8efba91da1a..599c59e4a2e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
@@ -17,19 +17,17 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "bmesh.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = {
{SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024},
- {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 3, 1024},
+ {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 2, 1024},
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
{-1, ""},
};
@@ -65,31 +63,225 @@ static int sphere_face_total(const int segments, const int rings)
return quads + triangles;
}
-static Mesh *create_uv_sphere_mesh_bmesh(const float radius, const int segments, const int rings)
+static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
+ const float radius,
+ const int segments,
+ const int rings)
+{
+ const float delta_theta = M_PI / rings;
+ const float delta_phi = (2 * M_PI) / segments;
+
+ copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
+ normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f));
+
+ int vert_index = 1;
+ float theta = delta_theta;
+ for (const int UNUSED(ring) : IndexRange(rings - 1)) {
+ float phi = 0.0f;
+ const float z = cosf(theta);
+ for (const int UNUSED(segment) : IndexRange(segments)) {
+ const float sin_theta = std::sin(theta);
+ const float x = sin_theta * std::cos(phi);
+ const float y = sin_theta * std::sin(phi);
+ copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
+ normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z));
+ phi += delta_phi;
+ vert_index++;
+ }
+ theta += delta_theta;
+ }
+
+ copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius));
+ normal_float_to_short_v3(verts.last().no, float3(0.0f, 0.0f, -1.0f));
+}
+
+static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
+ const int segments,
+ const int rings)
+{
+ int edge_index = 0;
+
+ /* Add the edges connecting the top vertex to the first ring. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = 0;
+ edge.v2 = first_vert_ring_index_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ int ring_vert_index_start = 1;
+ for (const int ring : IndexRange(rings - 1)) {
+ const int next_ring_vert_index_start = ring_vert_index_start + segments;
+
+ /* Add the edges running along each ring. */
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = ring_vert_index_start + segment;
+ edge.v2 = ring_vert_index_start + ((segment + 1) % segments);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ /* Add the edges connecting to the next ring. */
+ if (ring < rings - 2) {
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = ring_vert_index_start + segment;
+ edge.v2 = next_ring_vert_index_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ ring_vert_index_start += segments;
+ }
+
+ /* Add the edges connecting the last ring to the bottom vertex. */
+ const int last_vert_index = sphere_vert_total(segments, rings) - 1;
+ const int last_vert_ring_start = last_vert_index - segments;
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = last_vert_index;
+ edge.v2 = last_vert_ring_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+}
+
+static void calculate_sphere_faces(MutableSpan<MLoop> loops,
+ MutableSpan<MPoly> polys,
+ const int segments,
+ const int rings)
{
- const float4x4 transform = float4x4::identity();
-
- const BMeshCreateParams bmcp = {true};
- const BMAllocTemplate allocsize = {sphere_vert_total(segments, rings),
- sphere_edge_total(segments, rings),
- sphere_corner_total(segments, rings),
- sphere_face_total(segments, rings)};
- BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
-
- BMO_op_callf(bm,
- BMO_FLAG_DEFAULTS,
- "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b",
- segments,
- rings,
- radius,
- transform.values,
- true);
-
- BMeshToMeshParams params{};
- params.calc_object_remap = false;
- Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
- BM_mesh_free(bm);
+ int loop_index = 0;
+ int poly_index = 0;
+
+ /* Add the triangles connected to the top vertex. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = 0;
+ loop_a.e = segment;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = first_vert_ring_index_start + segment;
+ loop_b.e = segments + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = first_vert_ring_index_start + (segment + 1) % segments;
+ loop_c.e = (segment + 1) % segments;
+ }
+
+ int ring_vert_index_start = 1;
+ int ring_edge_index_start = segments;
+ for (const int UNUSED(ring) : IndexRange(1, rings - 2)) {
+ const int next_ring_vert_index_start = ring_vert_index_start + segments;
+ const int next_ring_edge_index_start = ring_edge_index_start + segments * 2;
+ const int ring_vertical_edge_index_start = ring_edge_index_start + segments;
+
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = ring_vert_index_start + segment;
+ loop_a.e = ring_vertical_edge_index_start + segment;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = next_ring_vert_index_start + segment;
+ loop_b.e = next_ring_edge_index_start + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = next_ring_vert_index_start + (segment + 1) % segments;
+ loop_c.e = ring_vertical_edge_index_start + (segment + 1) % segments;
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = ring_vert_index_start + (segment + 1) % segments;
+ loop_d.e = ring_edge_index_start + segment;
+ }
+ ring_vert_index_start += segments;
+ ring_edge_index_start += segments * 2;
+ }
+
+ /* Add the triangles connected to the bottom vertex. */
+ const int last_edge_ring_start = segments * (rings - 2) * 2 + segments;
+ const int bottom_edge_fan_start = last_edge_ring_start + segments;
+ const int last_vert_index = sphere_vert_total(segments, rings) - 1;
+ const int last_vert_ring_start = last_vert_index - segments;
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = last_vert_index;
+ loop_a.e = bottom_edge_fan_start + (segment + 1) % segments;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = last_vert_ring_start + (segment + 1) % segments;
+ loop_b.e = last_edge_ring_start + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = last_vert_ring_start + segment;
+ loop_c.e = bottom_edge_fan_start + segment;
+ }
+}
+
+static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ int loop_index = 0;
+ const float dy = 1.0f / rings;
+
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2((segment + 0.5f) / segments, 0.0f);
+ uvs[loop_index++] = float2(segment / segments, dy);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, dy);
+ }
+
+ for (const int i_ring : IndexRange(1, rings - 2)) {
+ const float ring = static_cast<float>(i_ring);
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2(segment / segments, ring / rings);
+ uvs[loop_index++] = float2(segment / segments, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, ring / rings);
+ }
+ }
+
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2((segment + 0.5f) / segments, 1.0f);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, 1.0f - dy);
+ uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
+ }
+
+ uv_attribute.save();
+}
+
+static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
+{
+ Mesh *mesh = BKE_mesh_new_nomain(sphere_vert_total(segments, rings),
+ sphere_edge_total(segments, rings),
+ 0,
+ sphere_corner_total(segments, rings),
+ sphere_face_total(segments, rings));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ calculate_sphere_vertex_data(verts, radius, segments, rings);
+
+ calculate_sphere_edge_indices(edges, segments, rings);
+
+ calculate_sphere_faces(loops, polys, segments, rings);
+
+ calculate_sphere_uvs(mesh, segments, rings);
+
+ BLI_assert(BKE_mesh_is_valid(mesh));
return mesh;
}
@@ -98,14 +290,14 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
{
const int segments_num = params.extract_input<int>("Segments");
const int rings_num = params.extract_input<int>("Rings");
- if (segments_num < 3 || rings_num < 3) {
+ if (segments_num < 3 || rings_num < 2) {
params.set_output("Geometry", GeometrySet());
return;
}
const float radius = params.extract_input<float>("Radius");
- Mesh *mesh = create_uv_sphere_mesh_bmesh(radius, segments_num, rings_num);
+ Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
new file mode 100644
index 00000000000..0fb7910c904
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -0,0 +1,316 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_task.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+template<typename T>
+static void copy_attribute_to_points(const VArray<T> &source_data,
+ Span<int> map,
+ MutableSpan<T> dest_data)
+{
+ for (const int point_index : map.index_range()) {
+ const int vert_index = map[point_index];
+ dest_data[point_index] = source_data[vert_index];
+ }
+}
+
+static void copy_attributes_to_points(CurveEval &curve,
+ const MeshComponent &mesh_component,
+ Span<Vector<int>> point_to_vert_maps)
+{
+ MutableSpan<SplinePtr> splines = curve.splines();
+ Set<std::string> source_attribute_names = mesh_component.attribute_names();
+
+ /* Copy builtin control point attributes. */
+ if (source_attribute_names.contains_as("tilt")) {
+ const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
+ "tilt", ATTR_DOMAIN_POINT, 0.0f);
+ parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ copy_attribute_to_points<float>(
+ *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
+ }
+ });
+ source_attribute_names.remove_contained_as("tilt");
+ }
+ if (source_attribute_names.contains_as("radius")) {
+ const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
+ "radius", ATTR_DOMAIN_POINT, 1.0f);
+ parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ copy_attribute_to_points<float>(
+ *radius_attribute, point_to_vert_maps[i], splines[i]->radii());
+ }
+ });
+ source_attribute_names.remove_contained_as("radius");
+ }
+
+ /* Don't copy other builtin control point attributes. */
+ source_attribute_names.remove_as("position");
+
+ /* Copy dynamic control point attributes. */
+ for (const StringRef name : source_attribute_names) {
+ const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name,
+ ATTR_DOMAIN_POINT);
+ /* Some attributes might not exist if they were builtin attribute on domains that don't
+ * have any elements, i.e. a face attribute on the output of the line primitive node. */
+ if (!mesh_attribute) {
+ continue;
+ }
+
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type());
+
+ parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ /* Create attribute on the spline points. */
+ splines[i]->attributes.create(name, data_type);
+ std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(name);
+ BLI_assert(spline_attribute);
+
+ /* Copy attribute based on the map for this spline. */
+ attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_attribute_to_points<T>(
+ mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>());
+ });
+ }
+ });
+ }
+
+ curve.assert_valid_point_attributes();
+}
+
+struct CurveFromEdgesOutput {
+ std::unique_ptr<CurveEval> curve;
+ Vector<Vector<int>> point_to_vert_maps;
+};
+
+static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+ Vector<Vector<int>> point_to_vert_maps;
+
+ /* Compute the number of edges connecting to each vertex. */
+ Array<int> neighbor_count(verts.size(), 0);
+ for (const std::pair<int, int> &edge : edges) {
+ neighbor_count[edge.first]++;
+ neighbor_count[edge.second]++;
+ }
+
+ /* Compute an offset into the array of neighbor edges based on the counts. */
+ Array<int> neighbor_offsets(verts.size());
+ int start = 0;
+ for (const int i : verts.index_range()) {
+ neighbor_offsets[i] = start;
+ start += neighbor_count[i];
+ }
+
+ /* Use as an index into the "neighbor group" for each vertex. */
+ Array<int> used_slots(verts.size(), 0);
+ /* Calculate the indices of each vertex's neighboring edges. */
+ Array<int> neighbors(edges.size() * 2);
+ for (const int i : edges.index_range()) {
+ const int v1 = edges[i].first;
+ const int v2 = edges[i].second;
+ neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2;
+ neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1;
+ used_slots[v1]++;
+ used_slots[v2]++;
+ }
+
+ /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */
+ Array<int> unused_edges = std::move(used_slots);
+
+ for (const int start_vert : verts.index_range()) {
+ /* The vertex will be part of a cyclic spline. */
+ if (neighbor_count[start_vert] == 2) {
+ continue;
+ }
+
+ /* The vertex has no connected edges, or they were already used. */
+ if (unused_edges[start_vert] == 0) {
+ continue;
+ }
+
+ for (const int i : IndexRange(neighbor_count[start_vert])) {
+ int current_vert = start_vert;
+ int next_vert = neighbors[neighbor_offsets[current_vert] + i];
+
+ if (unused_edges[next_vert] == 0) {
+ continue;
+ }
+
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Vector<int> point_to_vert_map;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+
+ /* Follow connected edges until we read a vertex with more than two connected edges. */
+ while (true) {
+ int last_vert = current_vert;
+ current_vert = next_vert;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+ unused_edges[current_vert]--;
+ unused_edges[last_vert]--;
+
+ if (neighbor_count[current_vert] != 2) {
+ break;
+ }
+
+ const int offset = neighbor_offsets[current_vert];
+ const int next_a = neighbors[offset];
+ const int next_b = neighbors[offset + 1];
+ next_vert = (last_vert == next_a) ? next_b : next_a;
+ }
+
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ point_to_vert_maps.append(std::move(point_to_vert_map));
+ }
+ }
+
+ /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */
+ for (const int start_vert : verts.index_range()) {
+ if (unused_edges[start_vert] != 2) {
+ continue;
+ }
+
+ int current_vert = start_vert;
+ int next_vert = neighbors[neighbor_offsets[current_vert]];
+
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Vector<int> point_to_vert_map;
+ spline->set_cyclic(true);
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+
+ /* Follow connected edges until we loop back to the start vertex. */
+ while (next_vert != start_vert) {
+ const int last_vert = current_vert;
+ current_vert = next_vert;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+ unused_edges[current_vert]--;
+ unused_edges[last_vert]--;
+
+ const int offset = neighbor_offsets[current_vert];
+ const int next_a = neighbors[offset];
+ const int next_b = neighbors[offset + 1];
+ next_vert = (last_vert == next_a) ? next_b : next_a;
+ }
+
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ point_to_vert_maps.append(std::move(point_to_vert_map));
+ }
+
+ curve->attributes.reallocate(curve->splines().size());
+ return {std::move(curve), std::move(point_to_vert_maps)};
+}
+
+/**
+ * Get a separate array of the indices for edges in a selection (a boolean attribute).
+ * This helps to make the above algorithm simpler by removing the need to check for selection
+ * in many places.
+ */
+static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params,
+ const MeshComponent &component)
+{
+ const Mesh &mesh = *component.get_for_read();
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ if (!selection_name.empty() && !component.attribute_exists(selection_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + selection_name + "\"");
+ }
+ GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
+ selection_name, ATTR_DOMAIN_EDGE, true);
+
+ Vector<std::pair<int, int>> selected_edges;
+ for (const int i : IndexRange(mesh.totedge)) {
+ if (selection[i]) {
+ selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2});
+ }
+ }
+
+ return selected_edges;
+}
+
+static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Curve", GeometrySet());
+ return;
+ }
+
+ const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *component.get_for_read();
+ Span<MVert> verts = Span{mesh.mvert, mesh.totvert};
+ Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component);
+ if (selected_edges.size() == 0) {
+ params.set_output("Curve", GeometrySet());
+ return;
+ }
+
+ CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges);
+ copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps);
+
+ params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_mesh_to_curve()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index 54ecb20dae7..167812d5656 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -14,20 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_mesh.h"
-#include "BKE_mesh_wrapper.h"
-#include "BKE_modifier.h"
-#include "BKE_volume.h"
-
#include "BLI_math_matrix.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_object_info_in[] = {
- {SOCK_OBJECT, N_("Object")},
+ {SOCK_OBJECT, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL},
{-1, ""},
};
@@ -52,9 +47,7 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = params.get_input<Object *>("Object");
float3 location = {0, 0, 0};
float3 rotation = {0, 0, 0};
@@ -78,14 +71,15 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
if (object != self_object) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ const int handle = instances.add_reference(*object);
if (transform_space_relative) {
- instances.add_instance(object, transform);
+ instances.add_instance(handle, transform);
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
- instances.add_instance(object, unit_transform);
+ instances.add_instance(handle, unit_transform);
}
}
}
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 2e3460ee5fe..772bd8a1080 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -14,12 +14,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_float3.hh"
#include "BLI_hash.h"
#include "BLI_kdtree.h"
-#include "BLI_math_vector.h"
#include "BLI_rand.hh"
-#include "BLI_span.hh"
#include "BLI_timeit.hh"
#include "DNA_mesh_types.h"
@@ -28,10 +25,10 @@
#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
-#include "BKE_deform.h"
#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
#include "BKE_pointcloud.h"
#include "UI_interface.h"
@@ -44,7 +41,7 @@ using blender::bke::GeometryInstanceGroup;
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
+ {SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_DISTANCE},
{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},
@@ -95,7 +92,7 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
- const FloatReadAttribute *density_factors,
+ const VArray<float> *density_factors,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
@@ -117,9 +114,9 @@ static void sample_mesh_surface(const Mesh &mesh,
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
- const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]);
- const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]);
- const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]);
+ const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
+ const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
+ const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
}
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
@@ -207,7 +204,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
const Mesh &mesh,
- const FloatReadAttribute &density_factors,
+ const VArray<float> &density_factors,
Span<float3> bary_coords,
Span<int> looptri_indices,
MutableSpan<bool> elimination_mask)
@@ -253,99 +250,27 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m
}
}
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totvert);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int v0_index = mesh.mloop[looptri.tri[0]].v;
- const int v1_index = mesh.mloop[looptri.tri[1]].v;
- const int v2_index = mesh.mloop[looptri.tri[2]].v;
-
- const T &v0 = data_in[v0_index];
- const T &v1 = data_in[v1_index];
- const T &v2 = data_in[v2_index];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totloop);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int loop_index_0 = looptri.tri[0];
- const int loop_index_1 = looptri.tri[1];
- const int loop_index_2 = looptri.tri[2];
-
- const T &v0 = data_in[loop_index_0];
- const T &v1 = data_in[loop_index_1];
- const T &v2 = data_in[loop_index_2];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_face(const Mesh &mesh,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totpoly);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : data_out.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const int poly_index = looptri.poly;
- data_out[i] = data_in[poly_index];
- }
-}
-
-template<typename T>
BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
Span<float3> bary_coords,
Span<int> looptri_indices,
const AttributeDomain source_domain,
- Span<T> source_span,
- MutableSpan<T> output_span)
+ const GVArray &source_data,
+ GMutableSpan output_data)
{
switch (source_domain) {
case ATTR_DOMAIN_POINT: {
- interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_point_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_CORNER: {
- interpolate_attribute_corner<T>(
- mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_corner_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_FACE: {
- interpolate_attribute_face<T>(mesh, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_face_attribute(
+ mesh, looptri_indices, source_data, output_data);
break;
}
default: {
@@ -367,13 +292,13 @@ BLI_NOINLINE static void interpolate_existing_attributes(
StringRef attribute_name = entry.key;
const CustomDataType output_data_type = entry.value.data_type;
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
+ OutputAttribute attribute_out = component.attribute_try_get_for_output_only(
attribute_name, ATTR_DOMAIN_POINT, output_data_type);
if (!attribute_out) {
continue;
}
- fn::GMutableSpan out_span = attribute_out->get_span_for_write_only();
+ GMutableSpan out_span = attribute_out.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -381,47 +306,41 @@ BLI_NOINLINE static void interpolate_existing_attributes(
const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *source_component.get_for_read();
- /* Use a dummy read without specifying a domain or data type in order to
- * get the existing attribute's domain. Interpolation is done manually based
- * on the bary coords in #interpolate_attribute. */
- ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read(
+ std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data(
attribute_name);
- if (!dummy_attribute) {
+ if (!attribute_info) {
i_instance += set_group.transforms.size();
continue;
}
- const AttributeDomain source_domain = dummy_attribute->domain();
- ReadAttributePtr source_attribute = source_component.attribute_get_for_read(
+ const AttributeDomain source_domain = attribute_info->domain;
+ GVArrayPtr source_attribute = source_component.attribute_get_for_read(
attribute_name, source_domain, output_data_type, nullptr);
if (!source_attribute) {
i_instance += set_group.transforms.size();
continue;
}
- fn::GSpan source_span = source_attribute->get_span();
+
+ for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ const int offset = instance_start_offsets[i_instance];
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+
+ GMutableSpan instance_span = out_span.slice(offset, bary_coords.size());
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span);
+
+ i_instance++;
+ }
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
using T = decltype(dummy);
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
- const int offset = instance_start_offsets[i_instance];
- Span<float3> bary_coords = bary_coords_array[i_instance];
- Span<int> looptri_indices = looptri_indices_array[i_instance];
-
- MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size());
- interpolate_attribute<T>(mesh,
- bary_coords,
- looptri_indices,
- source_domain,
- source_span.typed<T>(),
- instance_span);
-
- i_instance++;
- }
+ GVArray_Span<T> source_span{*source_attribute};
});
}
- attribute_out.apply_span_and_save();
+ attribute_out.save();
}
}
@@ -431,16 +350,16 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
{
- OutputAttributePtr id_attribute = component.attribute_try_get_for_output(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
- OutputAttributePtr normal_attribute = component.attribute_try_get_for_output(
- "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> normal_attribute =
+ component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT);
- MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>();
- MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>();
- MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>();
+ MutableSpan<int> result_ids = id_attribute.as_span();
+ MutableSpan<float3> result_normals = normal_attribute.as_span();
+ MutableSpan<float3> result_rotations = rotation_attribute.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
@@ -484,9 +403,9 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
}
}
- id_attribute.apply_span_and_save();
- normal_attribute.apply_span_and_save();
- rotation_attribute.apply_span_and_save();
+ id_attribute.save();
+ normal_attribute.save();
+ rotation_attribute.save();
}
BLI_NOINLINE static void add_remaining_point_attributes(
@@ -524,7 +443,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
for (const float4x4 &transform : set_group.transforms) {
@@ -534,7 +453,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
sample_mesh_surface(mesh,
transform,
density,
- &density_factors,
+ &*density_factors,
seed,
positions,
bary_coords,
@@ -593,7 +512,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
@@ -626,7 +545,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
const GeometryNodePointDistributeMode distribute_method =
static_cast<GeometryNodePointDistributeMode>(params.node().custom1);
- const int seed = params.get_input<int>("Seed");
+ const int seed = params.get_input<int>("Seed") * 5383843;
const float density = params.extract_input<float>("Density Max");
const std::string density_attribute_name = params.extract_input<std::string>(
"Density Attribute");
@@ -636,7 +555,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
return;
}
- Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set);
+ Vector<GeometryInstanceGroup> set_groups;
+ geometry_set_gather_instances(geometry_set, set_groups);
if (set_groups.is_empty()) {
params.set_output("Geometry", GeometrySet());
return;
@@ -715,8 +635,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
geometry_set_out.get_component_for_write<PointCloudComponent>();
Map<std::string, AttributeKind> attributes;
- bke::gather_attribute_info(
- attributes, {GEO_COMPONENT_TYPE_MESH}, set_groups, {"position", "normal", "id"});
+ bke::geometry_set_gather_instances_attribute_info(
+ set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes);
add_remaining_point_attributes(set_groups,
instance_start_offsets,
attributes,
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 dbbb73bd36d..e52ab1b2127 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -14,15 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BKE_mesh.h"
-#include "BKE_persistent_data_handle.hh"
-
#include "DNA_collection_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_pointcloud_types.h"
#include "BLI_hash.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -31,8 +26,17 @@
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_OBJECT, N_("Object")},
- {SOCK_COLLECTION, N_("Collection")},
+ {SOCK_OBJECT, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL},
+ {SOCK_COLLECTION,
+ N_("Collection"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
@@ -52,6 +56,15 @@ static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C)
namespace blender::nodes {
+static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
+ sizeof(NodeGeometryPointInstance), __func__);
+ data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
+ data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
+ node->storage = data;
+}
+
static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
{
bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
@@ -69,129 +82,133 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection);
}
-static void get_instanced_data__object(const GeoNodeExecParams &params,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams &params)
{
- bke::PersistentObjectHandle object_handle = params.get_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = params.extract_input<Object *>("Object");
if (object == params.self_object()) {
- object = nullptr;
+ return {};
}
if (object != nullptr) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- r_instances_data.fill(instance);
+ return {*object};
}
+ return {};
}
-static void get_instanced_data__collection(
- const GeoNodeExecParams &params,
- const GeometryComponent &component,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams &params)
{
const bNode &node = params.node();
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
- bke::PersistentCollectionHandle collection_handle =
- params.get_input<bke::PersistentCollectionHandle>("Collection");
- Collection *collection = params.handle_map().lookup(collection_handle);
+ Collection *collection = params.get_input<Collection *>("Collection");
if (collection == nullptr) {
- return;
+ return {};
}
if (BLI_listbase_is_empty(&collection->children) &&
BLI_listbase_is_empty(&collection->gobject)) {
params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty"));
- return;
+ return {};
}
- const bool use_whole_collection = (node_storage->flag &
- GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0;
- if (use_whole_collection) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
- r_instances_data.fill(instance);
+ if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) {
+ return {*collection};
}
- else {
- Vector<InstancedData> possible_instances;
- /* Direct child objects are instanced as objects. */
- LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
- Object *object = cob->ob;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- possible_instances.append(instance);
- }
- /* Direct child collections are instanced as collections. */
- LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- Collection *child_collection = child->collection;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = child_collection;
- possible_instances.append(instance);
- }
- if (!possible_instances.is_empty()) {
- const int seed = params.get_input<int>("Seed");
- Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT);
- for (const int i : r_instances_data.index_range()) {
- const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size();
- r_instances_data[i] = possible_instances[index];
- }
- }
+ Vector<InstanceReference> references;
+ /* Direct child objects are instanced as objects. */
+ LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
+ references.append(*cob->ob);
+ }
+ /* Direct child collections are instanced as collections. */
+ LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
+ references.append(*child->collection);
}
+
+ return references;
}
-static Array<std::optional<InstancedData>> get_instanced_data(const GeoNodeExecParams &params,
- const GeometryComponent &component,
- const int amount)
+static Vector<InstanceReference> get_instance_references(GeoNodeExecParams &params)
{
const bNode &node = params.node();
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)
node_storage->instance_type;
- Array<std::optional<InstancedData>> instances_data(amount);
switch (type) {
case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: {
- get_instanced_data__object(params, instances_data);
- break;
+ return get_instance_references__object(params);
}
case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: {
- get_instanced_data__collection(params, component, instances_data);
- break;
+ return get_instance_references__collection(params);
}
}
- return instances_data;
+ return {};
+}
+
+/**
+ * Add the instance references to the component as a separate step from actually creating the
+ * instances in order to avoid a map lookup for every transform. While this might add some
+ * unnecessary references if they are not chosen while adding transforms, in the common cases
+ * there are many more transforms than there are references, so that isn't likely.
+ */
+static Array<int> add_instance_references(InstancesComponent &instance_component,
+ Span<InstanceReference> possible_references)
+{
+ Array<int> possible_handles(possible_references.size());
+ for (const int i : possible_references.index_range()) {
+ possible_handles[i] = instance_component.add_reference(possible_references[i]);
+ }
+ return possible_handles;
}
-static void add_instances_from_geometry_component(InstancesComponent &instances,
- const GeometryComponent &src_geometry,
- const GeoNodeExecParams &params)
+static void add_instances_from_component(InstancesComponent &instances,
+ const GeometryComponent &src_geometry,
+ Span<int> possible_handles,
+ const GeoNodeExecParams &params)
{
const AttributeDomain domain = ATTR_DOMAIN_POINT;
const int domain_size = src_geometry.attribute_domain_size(domain);
- Array<std::optional<InstancedData>> instances_data = get_instanced_data(
- params, src_geometry, domain_size);
- Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
- Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", domain, {0, 0, 0});
- Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
"scale", domain, {1, 1, 1});
- Int32ReadAttribute ids = src_geometry.attribute_get_for_read<int>("id", domain, -1);
-
- for (const int i : IndexRange(domain_size)) {
- if (instances_data[i].has_value()) {
- float transform[4][4];
- loc_eul_size_to_mat4(transform, positions[i], rotations[i], scales[i]);
- instances.add_instance(*instances_data[i], transform, ids[i]);
- }
+ GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
+
+ /* The initial size of the component might be non-zero if there are two component types. */
+ const int start_len = instances.instances_amount();
+ instances.resize(start_len + domain_size);
+ MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size);
+ MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size);
+ MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size);
+
+ /* Skip all of the randomness handling if there is only a single possible instance
+ * (anything except for collection mode with "Whole Collection" turned off). */
+ if (possible_handles.size() == 1) {
+ const int handle = possible_handles.first();
+ parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ handles[i] = handle;
+ transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
+ instance_ids[i] = id_attribute[i];
+ }
+ });
+ }
+ else {
+ const int seed = params.get_input<int>("Seed");
+ Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT);
+ parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size();
+ const int handle = possible_handles[index];
+ handles[i] = handle;
+ transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
+ instance_ids[i] = id_attribute[i];
+ }
+ });
}
}
@@ -204,28 +221,37 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params)
* rather than making the entire input geometry set real. */
geometry_set = geometry_set_realize_instances(geometry_set);
+ const Vector<InstanceReference> possible_references = get_instance_references(params);
+ if (possible_references.is_empty()) {
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ Array<int> possible_handles = add_instance_references(instances, possible_references);
+
if (geometry_set.has<MeshComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<MeshComponent>(), params);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<MeshComponent>(),
+ possible_handles,
+ params);
}
if (geometry_set.has<PointCloudComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<PointCloudComponent>(),
+ possible_handles,
+ params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<CurveComponent>(),
+ possible_handles,
+ params);
}
params.set_output("Geometry", std::move(geometry_set_out));
}
-static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
- sizeof(NodeGeometryPointInstance), __func__);
- data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
- data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
- node->storage = data;
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_instance()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
index 5eef2fabce0..828d3f50551 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
@@ -14,13 +14,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_rotation.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_rotate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Axis")},
@@ -59,9 +59,43 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
+static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
+ sizeof(NodeGeometryRotatePoints), __func__);
+
+ node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
+ node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
+ node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Axis",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Angle",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Rotation",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER);
+}
+
static void point_rotate__axis_angle__object_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -76,8 +110,8 @@ static void point_rotate__axis_angle__object_space(const int domain_size,
}
static void point_rotate__axis_angle__point_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -92,7 +126,7 @@ static void point_rotate__axis_angle__point_space(const int domain_size,
}
static void point_rotate__euler__object_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -107,7 +141,7 @@ static void point_rotate__euler__object_space(const int domain_size,
}
static void point_rotate__euler__point_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -127,19 +161,19 @@ static void point_rotate_on_component(GeometryComponent &component,
const bNode &node = params.node();
const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!rotation_attribute) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
+ MutableSpan<float3> rotations = rotation_attribute.as_span();
const int domain_size = rotations.size();
if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) {
- Float3ReadAttribute axis = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> axis = params.get_input_attribute<float3>(
"Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1});
- FloatReadAttribute angles = params.get_input_attribute<float>(
+ GVArray_Typed<float> angles = params.get_input_attribute<float>(
"Angle", component, ATTR_DOMAIN_POINT, 0);
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -150,7 +184,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
else {
- Float3ReadAttribute eulers = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> eulers = params.get_input_attribute<float3>(
"Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -161,7 +195,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
- rotation_attribute.apply_span_and_save();
+ rotation_attribute.save();
}
static void geo_node_point_rotate_exec(GeoNodeExecParams params)
@@ -176,44 +210,13 @@ static void geo_node_point_rotate_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
point_rotate_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ point_rotate_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
- sizeof(NodeGeometryRotatePoints), __func__);
-
- node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
- node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
- node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
- node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
- update_attribute_input_socket_availabilities(
- *node,
- "Axis",
- (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
- update_attribute_input_socket_availabilities(
- *node,
- "Angle",
- (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
- update_attribute_input_socket_availabilities(
- *node,
- "Rotation",
- (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER);
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_rotate()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
index 9df103ff057..265ec9fb776 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
@@ -14,17 +14,18 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BKE_colorband.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_scale_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Factor")},
{SOCK_VECTOR, N_("Factor"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {SOCK_FLOAT, N_("Factor"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{-1, ""},
};
@@ -42,27 +43,64 @@ static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), P
namespace blender::nodes {
+static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
+ sizeof(NodeGeometryPointScale), __func__);
+
+ data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node->storage = data;
+}
+
+static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
+}
+
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
+ /* Note that scale doesn't necessarily need to be created with a vector type-- it could also use
+ * the highest complexity of the existing attribute's type (if it exists) and the data type used
+ * for the factor. But for it's simpler to simply always use float3, since that is usually
+ * expected anyway. */
static const float3 scale_default = float3(1.0f);
- OutputAttributePtr scale_attribute = component.attribute_try_get_for_output(
+ OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output(
"scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default);
if (!scale_attribute) {
return;
}
- ReadAttributePtr attribute = params.get_input_attribute(
- "Factor", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
+
+ const bNode &node = params.node();
+ const NodeGeometryPointScale &node_storage = *(const NodeGeometryPointScale *)node.storage;
+ const GeometryNodeAttributeInputMode input_type = (GeometryNodeAttributeInputMode)
+ node_storage.input_type;
+ const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT :
+ CD_PROP_FLOAT3;
+
+ GVArrayPtr attribute = params.get_input_attribute(
+ "Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr);
if (!attribute) {
return;
}
- Span<float3> data = attribute->get_span<float3>();
- MutableSpan<float3> scale_span = scale_attribute->get_span<float3>();
- for (const int i : scale_span.index_range()) {
- scale_span[i] = scale_span[i] * data[i];
+ MutableSpan<float3> scale_span = scale_attribute.as_span();
+ if (data_type == CD_PROP_FLOAT) {
+ GVArray_Typed<float> factors{*attribute};
+ for (const int i : scale_span.index_range()) {
+ scale_span[i] = scale_span[i] * factors[i];
+ }
+ }
+ else if (data_type == CD_PROP_FLOAT3) {
+ GVArray_Typed<float3> factors{*attribute};
+ for (const int i : scale_span.index_range()) {
+ scale_span[i] = scale_span[i] * factors[i];
+ }
}
- scale_attribute.apply_span_and_save();
+ scale_attribute.save();
}
static void geo_node_point_scale_exec(GeoNodeExecParams params)
@@ -77,27 +115,13 @@ static void geo_node_point_scale_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
-static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
- sizeof(NodeGeometryPointScale), __func__);
-
- data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- node->storage = data;
-}
-
-static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
-
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_scale()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
index 522dea4aa0e..fc04d1e275f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
@@ -52,33 +52,33 @@ static void copy_data_based_on_mask(Span<T> data,
}
}
-static void copy_attributes_based_on_mask(const GeometryComponent &in_component,
- GeometryComponent &result_component,
- Span<bool> masks,
- const bool invert)
+void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ Span<bool> masks,
+ const bool invert)
{
for (const std::string &name : in_component.attribute_names()) {
- ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name);
- const CustomDataType data_type = attribute->custom_data_type();
+ ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name);
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type());
/* Only copy point attributes. Theoretically this could interpolate attributes on other
* domains to the point domain, but that would conflict with attributes that are built-in
* on other domains, which causes creating the attributes to fail. */
- if (attribute->domain() != ATTR_DOMAIN_POINT) {
+ if (attribute.domain != ATTR_DOMAIN_POINT) {
continue;
}
- OutputAttributePtr result_attribute = result_component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
name, ATTR_DOMAIN_POINT, data_type);
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- Span<T> span = attribute->get_span<T>();
- MutableSpan<T> out_span = result_attribute->get_span_for_write_only<T>();
+ GVArray_Span<T> span{*attribute.varray};
+ MutableSpan<T> out_span = result_attribute.as_span<T>();
copy_data_based_on_mask(span, masks, invert, out_span);
});
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
}
@@ -107,9 +107,9 @@ static void separate_points_from_component(const GeometryComponent &in_component
return;
}
- const BooleanReadAttribute mask_attribute = in_component.attribute_get_for_read<bool>(
+ const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_POINT, false);
- Span<bool> masks = mask_attribute.get_span();
+ VArray_Span<bool> masks{mask_attribute};
const int total = masks.count(!invert);
if (total == 0) {
@@ -118,7 +118,7 @@ static void separate_points_from_component(const GeometryComponent &in_component
create_component_points(out_component, total);
- copy_attributes_based_on_mask(in_component, out_component, masks, invert);
+ copy_point_attributes_based_on_mask(in_component, out_component, masks, invert);
}
static GeometrySet separate_geometry_set(const GeometrySet &set_in,
@@ -127,6 +127,10 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
{
GeometrySet set_out;
for (const GeometryComponent *component : set_in.get_components_for_read()) {
+ if (component->type() == GEO_COMPONENT_TYPE_CURVE) {
+ /* Don't support the curve component for now, even though it has a point domain. */
+ continue;
+ }
GeometryComponent &out_component = set_out.get_component_for_write(component->type());
separate_points_from_component(*component, out_component, mask_name, invert);
}
@@ -135,15 +139,27 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
static void geo_node_point_separate_exec(GeoNodeExecParams params)
{
- const std::string mask_attribute_name = params.extract_input<std::string>("Mask");
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ bool wait_for_inputs = false;
+ wait_for_inputs |= params.lazy_require_input("Geometry");
+ wait_for_inputs |= params.lazy_require_input("Mask");
+ if (wait_for_inputs) {
+ return;
+ }
+ const std::string mask_attribute_name = params.get_input<std::string>("Mask");
+ GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
/* TODO: This is not necessary-- the input geometry set can be read only,
* but it must be rewritten to handle instance groups. */
geometry_set = geometry_set_realize_instances(geometry_set);
- params.set_output("Geometry 1", separate_geometry_set(geometry_set, mask_attribute_name, true));
- params.set_output("Geometry 2", separate_geometry_set(geometry_set, mask_attribute_name, false));
+ if (params.lazy_output_is_required("Geometry 1")) {
+ params.set_output("Geometry 1",
+ separate_geometry_set(geometry_set, mask_attribute_name, true));
+ }
+ if (params.lazy_output_is_required("Geometry 2")) {
+ params.set_output("Geometry 2",
+ separate_geometry_set(geometry_set, mask_attribute_name, false));
+ }
}
} // namespace blender::nodes
@@ -155,5 +171,6 @@ void register_node_type_geo_point_separate()
geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec;
+ ntype.geometry_node_execute_supports_laziness = true;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
index 015f4cd38e7..293f151fe19 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
@@ -14,13 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_colorband.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_translate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Translation")},
@@ -44,24 +42,19 @@ namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
- OutputAttributePtr position_attribute = component.attribute_try_get_for_output(
- "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> position_attribute =
+ component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!position_attribute) {
return;
}
- ReadAttributePtr attribute = params.get_input_attribute(
- "Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
- if (!attribute) {
- return;
- }
+ GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
+ "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
- Span<float3> data = attribute->get_span<float3>();
- MutableSpan<float3> scale_span = position_attribute->get_span<float3>();
- for (const int i : scale_span.index_range()) {
- scale_span[i] = scale_span[i] + data[i];
+ for (const int i : IndexRange(attribute.size())) {
+ position_attribute->set(i, position_attribute->get(i) + attribute[i]);
}
- position_attribute.apply_span_and_save();
+ position_attribute.save();
}
static void geo_node_point_translate_exec(GeoNodeExecParams params)
@@ -76,6 +69,9 @@ static void geo_node_point_translate_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
@@ -85,7 +81,7 @@ static void geo_node_point_translate_init(bNodeTree *UNUSED(tree), bNode *node)
NodeGeometryPointTranslate *data = (NodeGeometryPointTranslate *)MEM_callocN(
sizeof(NodeGeometryPointTranslate), __func__);
- data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node->storage = data;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
index b79ca0a6197..65306b1c452 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
@@ -31,14 +31,14 @@
static bNodeSocketTemplate geo_node_points_to_volume_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
- {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX},
+ {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_STRING, N_("Radius")},
{SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{-1, ""},
};
-static bNodeSocketTemplate geo_node_point_translate_out[] = {
+static bNodeSocketTemplate geo_node_points_to_volume_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
@@ -55,6 +55,35 @@ static void geo_node_points_to_volume_layout(uiLayout *layout,
namespace blender::nodes {
+static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
+ sizeof(NodeGeometryPointsToVolume), __func__);
+ data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
+ data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node->storage = data;
+
+ bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
+ bNodeSocketValueString *radius_attribute_socket_value =
+ (bNodeSocketValueString *)radius_attribute_socket->default_value;
+ STRNCPY(radius_attribute_socket_value->value, "radius");
+}
+
+static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
+ bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
+ bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
+ nodeSetSocketAvailability(voxel_amount_socket,
+ data->resolution_mode ==
+ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
+ nodeSetSocketAvailability(
+ voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
+
+ update_attribute_input_socket_availabilities(
+ *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
+}
+
#ifdef WITH_OPENVDB
namespace {
/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */
@@ -147,13 +176,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams &params,
Vector<float3> &r_positions,
Vector<float> &r_radii)
{
- Float3ReadAttribute positions = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- FloatReadAttribute radii = params.get_input_attribute<float>(
+ GVArray_Typed<float> radii = params.get_input_attribute<float>(
"Radius", component, ATTR_DOMAIN_POINT, 0.0f);
- r_positions.extend(positions.get_span());
- r_radii.extend(radii.get_span());
+ for (const int i : IndexRange(positions.size())) {
+ r_positions.append(positions[i]);
+ r_radii.append(radii[i]);
+ }
}
static void convert_to_grid_index_space(const float voxel_size,
@@ -184,6 +215,10 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_
gather_point_data_from_component(
params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii);
}
+ if (geometry_set_in.has<CurveComponent>()) {
+ gather_point_data_from_component(
+ params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii);
+ }
const float max_radius = *std::max_element(radii.begin(), radii.end());
const float voxel_size = compute_voxel_size(params, positions, max_radius);
@@ -225,35 +260,6 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set_out));
}
-static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
- sizeof(NodeGeometryPointsToVolume), __func__);
- data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
- data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node->storage = data;
-
- bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
- bNodeSocketValueString *radius_attribute_socket_value =
- (bNodeSocketValueString *)radius_attribute_socket->default_value;
- STRNCPY(radius_attribute_socket_value->value, "radius");
-}
-
-static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
- bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
- bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
- nodeSetSocketAvailability(voxel_amount_socket,
- data->resolution_mode ==
- GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
- nodeSetSocketAvailability(
- voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
-
- update_attribute_input_socket_availabilities(
- *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
-}
-
} // namespace blender::nodes
void register_node_type_geo_points_to_volume()
@@ -262,7 +268,7 @@ void register_node_type_geo_points_to_volume()
geo_node_type_base(
&ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0);
- node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_point_translate_out);
+ node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_points_to_volume_out);
node_type_storage(&ntype,
"NodeGeometryPointsToVolume",
node_free_standard_storage,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
new file mode 100644
index 00000000000..9bc963eec43
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
@@ -0,0 +1,106 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_task.hh"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_select_by_material_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL,
+ N_("Material"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_select_by_material_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void select_mesh_by_material(const Mesh &mesh,
+ const Material *material,
+ const MutableSpan<bool> r_selection)
+{
+ BLI_assert(mesh.totpoly == r_selection.size());
+ Vector<int> material_indices;
+ for (const int i : IndexRange(mesh.totcol)) {
+ if (mesh.mat[i] == material) {
+ material_indices.append(i);
+ }
+ }
+ parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr);
+ }
+ });
+}
+
+static void geo_node_select_by_material_exec(GeoNodeExecParams params)
+{
+ Material *material = params.extract_input<Material *>("Material");
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh != nullptr) {
+ OutputAttribute_Typed<bool> selection =
+ mesh_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_FACE);
+ if (selection) {
+ select_mesh_by_material(*mesh, material, selection.as_span());
+ selection.save();
+ }
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_select_by_material()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_select_by_material_in, geo_node_select_by_material_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_select_by_material_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
index 06c5586a3ff..a4c6522c57a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
@@ -14,7 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
+// #include "MEM_guardedalloc.h"
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
@@ -41,6 +41,7 @@ namespace blender::nodes {
static void geo_node_subdivide_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", geometry_set);
@@ -49,7 +50,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
- TIP_("Disabled, Blender was built without OpenSubdiv"));
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
params.set_output("Geometry", std::move(geometry_set));
return;
#endif
@@ -98,6 +99,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_subdivide()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
index b14a512ce28..20ffcdb516d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -14,8 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
-
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.h"
@@ -39,17 +37,6 @@ static bNodeSocketTemplate geo_node_subdivision_surface_out[] = {
{-1, ""},
};
-static void geo_node_subdivision_surface_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr))
-{
-#ifndef WITH_OPENSUBDIV
- uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR);
-#else
- UNUSED_VARS(layout);
-#endif
-}
-
namespace blender::nodes {
static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
{
@@ -63,9 +50,8 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
}
#ifndef WITH_OPENSUBDIV
- /* Return input geometry if Blender is built without OpenSubdiv. */
- params.set_output("Geometry", std::move(geometry_set));
- return;
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
#else
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
@@ -115,9 +101,11 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
// BKE_subdiv_stats_print(&subdiv->stats);
BKE_subdiv_free(subdiv);
- params.set_output("Geometry", std::move(geometry_set));
#endif
+
+ params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_subdivision_surface()
@@ -129,6 +117,5 @@ void register_node_type_geo_subdivision_surface()
node_type_socket_templates(
&ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec;
- ntype.draw_buttons = geo_node_subdivision_surface_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
new file mode 100644
index 00000000000..0aa5c68aaf5
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -0,0 +1,191 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_switch_in[] = {
+ {SOCK_BOOLEAN, N_("Switch")},
+
+ {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_BOOLEAN, N_("False")},
+ {SOCK_BOOLEAN, N_("True")},
+ {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_STRING, N_("False")},
+ {SOCK_STRING, N_("True")},
+ {SOCK_GEOMETRY, N_("False")},
+ {SOCK_GEOMETRY, N_("True")},
+ {SOCK_OBJECT, N_("False")},
+ {SOCK_OBJECT, N_("True")},
+ {SOCK_COLLECTION, N_("False")},
+ {SOCK_COLLECTION, N_("True")},
+ {SOCK_TEXTURE, N_("False")},
+ {SOCK_TEXTURE, N_("True")},
+ {SOCK_MATERIAL, N_("False")},
+ {SOCK_MATERIAL, N_("True")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_switch_out[] = {
+ {SOCK_FLOAT, N_("Output")},
+ {SOCK_INT, N_("Output")},
+ {SOCK_BOOLEAN, N_("Output")},
+ {SOCK_VECTOR, N_("Output")},
+ {SOCK_RGBA, N_("Output")},
+ {SOCK_STRING, N_("Output")},
+ {SOCK_GEOMETRY, N_("Output")},
+ {SOCK_OBJECT, N_("Output")},
+ {SOCK_COLLECTION, N_("Output")},
+ {SOCK_TEXTURE, N_("Output")},
+ {SOCK_MATERIAL, N_("Output")},
+ {-1, ""},
+};
+
+static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__);
+ data->input_type = SOCK_GEOMETRY;
+ node->storage = data;
+}
+
+namespace blender::nodes {
+
+static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeSwitch *node_storage = (NodeSwitch *)node->storage;
+ int index = 0;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ nodeSetSocketAvailability(
+ socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ index++;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(socket,
+ socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ }
+}
+
+template<typename T>
+static void output_input(GeoNodeExecParams &params,
+ const bool input,
+ const StringRef input_suffix,
+ const StringRef output_identifier)
+{
+ const std::string name_a = "False" + input_suffix;
+ const std::string name_b = "True" + input_suffix;
+ if (input) {
+ params.set_input_unused(name_a);
+ if (params.lazy_require_input(name_b)) {
+ return;
+ }
+ params.set_output(output_identifier, params.extract_input<T>(name_b));
+ }
+ else {
+ params.set_input_unused(name_b);
+ if (params.lazy_require_input(name_a)) {
+ return;
+ }
+ params.set_output(output_identifier, params.extract_input<T>(name_a));
+ }
+}
+
+static void geo_node_switch_exec(GeoNodeExecParams params)
+{
+ if (params.lazy_require_input("Switch")) {
+ return;
+ }
+ const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage;
+ const bool input = params.get_input<bool>("Switch");
+ switch ((eNodeSocketDatatype)storage.input_type) {
+ case SOCK_FLOAT: {
+ output_input<float>(params, input, "", "Output");
+ break;
+ }
+ case SOCK_INT: {
+ output_input<int>(params, input, "_001", "Output_001");
+ break;
+ }
+ case SOCK_BOOLEAN: {
+ output_input<bool>(params, input, "_002", "Output_002");
+ break;
+ }
+ case SOCK_VECTOR: {
+ output_input<float3>(params, input, "_003", "Output_003");
+ break;
+ }
+ case SOCK_RGBA: {
+ output_input<ColorGeometry4f>(params, input, "_004", "Output_004");
+ break;
+ }
+ case SOCK_STRING: {
+ output_input<std::string>(params, input, "_005", "Output_005");
+ break;
+ }
+ case SOCK_GEOMETRY: {
+ output_input<GeometrySet>(params, input, "_006", "Output_006");
+ break;
+ }
+ case SOCK_OBJECT: {
+ output_input<Object *>(params, input, "_007", "Output_007");
+ break;
+ }
+ case SOCK_COLLECTION: {
+ output_input<Collection *>(params, input, "_008", "Output_008");
+ break;
+ }
+ case SOCK_TEXTURE: {
+ output_input<Tex *>(params, input, "_009", "Output_009");
+ break;
+ }
+ case SOCK_MATERIAL: {
+ output_input<Material *>(params, input, "_010", "Output_010");
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_switch()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0);
+ node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out);
+ node_type_init(&ntype, geo_node_switch_init);
+ node_type_update(&ntype, blender::nodes::geo_node_switch_update);
+ node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec;
+ ntype.geometry_node_execute_supports_laziness = true;
+ ntype.draw_buttons = geo_node_switch_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index 842a78b839a..cdd2df56b77 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -18,12 +18,14 @@
# include <openvdb/openvdb.h>
#endif
-#include "BLI_math_matrix.h"
+#include "BLI_float4x4.hh"
+#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_volume_types.h"
#include "BKE_mesh.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DEG_depsgraph_query.h"
@@ -81,14 +83,14 @@ void transform_mesh(Mesh *mesh,
/* Use only translation if rotation and scale are zero. */
if (use_translate(rotation, scale)) {
if (!translation.is_zero()) {
- BKE_mesh_translate(mesh, translation, true);
+ BKE_mesh_translate(mesh, translation, false);
}
}
else {
- float mat[4][4];
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
- BKE_mesh_transform(mesh, mat, true);
- BKE_mesh_calc_normals(mesh);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ BKE_mesh_transform(mesh, matrix.values, false);
+ mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
}
}
@@ -99,15 +101,15 @@ static void transform_pointcloud(PointCloud *pointcloud,
{
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
- for (int i = 0; i < pointcloud->totpoint; i++) {
+ for (const int i : IndexRange(pointcloud->totpoint)) {
add_v3_v3(pointcloud->co[i], translation);
}
}
else {
- float mat[4][4];
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
- for (int i = 0; i < pointcloud->totpoint; i++) {
- mul_m4_v3(mat, pointcloud->co[i]);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ for (const int i : IndexRange(pointcloud->totpoint)) {
+ float3 &co = *(float3 *)pointcloud->co[i];
+ co = matrix * co;
}
}
}
@@ -117,7 +119,7 @@ static void transform_instances(InstancesComponent &instances,
const float3 rotation,
const float3 scale)
{
- MutableSpan<float4x4> transforms = instances.transforms();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
@@ -126,11 +128,9 @@ static void transform_instances(InstancesComponent &instances,
}
}
else {
- float mat[4][4];
-
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
for (float4x4 &transform : transforms) {
- mul_m4_m4_pre(transform.ptr(), mat);
+ transform = matrix * transform;
}
}
}
@@ -149,11 +149,10 @@ static void transform_volume(Volume *volume,
(scale.z == 0.0f) ? FLT_EPSILON : scale.z,
};
- Main *bmain = DEG_get_bmain(params.depsgraph());
+ const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
- float matrix[4][4];
- loc_eul_size_to_mat4(matrix, translation, rotation, limited_scale);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, limited_scale);
openvdb::Mat4s vdb_matrix;
memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4]));
@@ -161,7 +160,7 @@ static void transform_volume(Volume *volume,
const int num_grids = BKE_volume_num_grids(volume);
for (const int i : IndexRange(num_grids)) {
- VolumeGrid *volume_grid = BKE_volume_grid_get(volume, i);
+ VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, i);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
openvdb::math::Transform &grid_transform = grid->transform();
@@ -172,6 +171,20 @@ static void transform_volume(Volume *volume,
#endif
}
+static void transform_curve(CurveEval &curve,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ if (use_translate(rotation, scale)) {
+ curve.translate(translation);
+ }
+ else {
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ curve.transform(matrix);
+ }
+}
+
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -183,21 +196,22 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
Mesh *mesh = geometry_set.get_mesh_for_write();
transform_mesh(mesh, translation, rotation, scale);
}
-
if (geometry_set.has_pointcloud()) {
PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
transform_pointcloud(pointcloud, translation, rotation, scale);
}
-
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
transform_instances(instances, translation, rotation, scale);
}
-
if (geometry_set.has_volume()) {
Volume *volume = geometry_set.get_volume_for_write();
transform_volume(volume, translation, rotation, scale, params);
}
+ if (geometry_set.has_curve()) {
+ CurveEval *curve = geometry_set.get_curve_for_write();
+ transform_curve(*curve, translation, rotation, scale);
+ }
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 0c754ad3b37..03581b28f18 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -14,10 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "DNA_node_types.h"
-
-#include "RNA_enum_types.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
index 54dccb613a1..403f4906d07 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
@@ -23,6 +23,7 @@
#include "node_geometry_util.hh"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_volume.h"
@@ -36,8 +37,8 @@
static bNodeSocketTemplate geo_node_volume_to_mesh_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Grid")},
- {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX},
+ {SOCK_STRING, N_("Density")},
+ {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Threshold"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Adaptivity"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
@@ -64,7 +65,7 @@ static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node)
sizeof(NodeGeometryVolumeToMesh), __func__);
data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID;
- bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Grid");
+ bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density");
bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value;
STRNCPY(grid_socket_value->value, "density");
@@ -117,11 +118,11 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in,
return;
}
- Main *bmain = DEG_get_bmain(params.depsgraph());
- BKE_volume_load(const_cast<Volume *>(volume), bmain);
+ const Main *bmain = DEG_get_bmain(params.depsgraph());
+ BKE_volume_load(volume, bmain);
- const std::string grid_name = params.get_input<std::string>("Grid");
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, grid_name.c_str());
+ const std::string grid_name = params.get_input<std::string>("Density");
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
if (volume_grid == nullptr) {
return;
}
@@ -134,6 +135,7 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in,
if (mesh == nullptr) {
return;
}
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>();
dst_component.replace(mesh);
}
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index e4f8a0c5fda..9a3eb574bcd 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -40,6 +40,7 @@ DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *paren
DTreeContext &context = *allocator_.construct<DTreeContext>().release();
context.parent_context_ = parent_context;
context.parent_node_ = parent_node;
+ context.derived_tree_ = this;
context.tree_ = &get_tree_ref_from_map(node_tree_refs, btree);
used_node_tree_refs_.add(context.tree_);
@@ -83,6 +84,16 @@ bool DerivedNodeTree::has_link_cycles() const
return false;
}
+bool DerivedNodeTree::has_undefined_nodes_or_sockets() const
+{
+ for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
+ if (tree_ref->has_undefined_nodes_or_sockets()) {
+ return true;
+ }
+ }
+ return false;
+}
+
/* Calls the given callback on all nodes in the (possibly nested) derived node tree. */
void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
{
@@ -167,10 +178,10 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
return {};
}
-/* Call the given callback for every "real" origin socket. "Real" means that reroutes, muted nodes
+/* Call `origin_fn` for every "real" origin socket. "Real" means that reroutes, muted nodes
* and node groups are handled by this function. Origin sockets are ones where a node gets its
* inputs from. */
-void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) const
+void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const
{
BLI_assert(*this);
for (const OutputSocketRef *linked_socket : socket_ref_->as_input().logically_linked_sockets()) {
@@ -180,18 +191,18 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) co
if (linked_node.is_group_input_node()) {
if (context_->is_root()) {
/* This is a group input in the root node group. */
- callback(linked_dsocket);
+ origin_fn(linked_dsocket);
}
else {
DInputSocket socket_in_parent_group = linked_dsocket.get_corresponding_group_node_input();
if (socket_in_parent_group->is_logically_linked()) {
/* Follow the links coming into the corresponding socket on the parent group node. */
- socket_in_parent_group.foreach_origin_socket(callback);
+ socket_in_parent_group.foreach_origin_socket(origin_fn);
}
else {
/* The corresponding input on the parent group node is not connected. Therefore, we use
* the value of that input socket directly. */
- callback(socket_in_parent_group);
+ origin_fn(socket_in_parent_group);
}
}
}
@@ -200,27 +211,32 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) co
if (socket_in_group) {
if (socket_in_group->is_logically_linked()) {
/* Follow the links coming into the group output node of the child node group. */
- socket_in_group.foreach_origin_socket(callback);
+ socket_in_group.foreach_origin_socket(origin_fn);
}
else {
/* The output of the child node group is not connected, so we have to get the value from
* that socket. */
- callback(socket_in_group);
+ origin_fn(socket_in_group);
}
}
}
else {
/* The normal case: just use the value of a linked output socket. */
- callback(linked_dsocket);
+ origin_fn(linked_dsocket);
}
}
}
-/* Calls the given callback for every "real" target socket. "Real" means that reroutes, muted nodes
+/* Calls `target_fn` for every "real" target socket. "Real" means that reroutes, muted nodes
* and node groups are handled by this function. Target sockets are on the nodes that use the value
- * from this socket. */
-void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const
+ * from this socket. The `skipped_fn` function is called for sockets that have been skipped during
+ * the search for target sockets (e.g. reroutes). */
+void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> target_fn,
+ FunctionRef<void(DSocket)> skipped_fn) const
{
+ for (const SocketRef *skipped_socket : socket_ref_->logically_linked_skipped_sockets()) {
+ skipped_fn.call_safe({context_, skipped_socket});
+ }
for (const InputSocketRef *linked_socket : socket_ref_->as_output().logically_linked_sockets()) {
const NodeRef &linked_node = linked_socket->node();
DInputSocket linked_dsocket{context_, linked_socket};
@@ -228,26 +244,30 @@ void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> callba
if (linked_node.is_group_output_node()) {
if (context_->is_root()) {
/* This is a group output in the root node group. */
- callback(linked_dsocket);
+ target_fn(linked_dsocket);
}
else {
/* Follow the links going out of the group node in the parent node group. */
DOutputSocket socket_in_parent_group =
linked_dsocket.get_corresponding_group_node_output();
- socket_in_parent_group.foreach_target_socket(callback);
+ skipped_fn.call_safe(linked_dsocket);
+ skipped_fn.call_safe(socket_in_parent_group);
+ socket_in_parent_group.foreach_target_socket(target_fn, skipped_fn);
}
}
else if (linked_node.is_group_node()) {
/* Follow the links within the nested node group. */
Vector<DOutputSocket> sockets_in_group =
linked_dsocket.get_corresponding_group_input_sockets();
+ skipped_fn.call_safe(linked_dsocket);
for (DOutputSocket socket_in_group : sockets_in_group) {
- socket_in_group.foreach_target_socket(callback);
+ skipped_fn.call_safe(socket_in_group);
+ socket_in_group.foreach_target_socket(target_fn, skipped_fn);
}
}
else {
/* The normal case: just use the linked input socket as target. */
- callback(linked_dsocket);
+ target_fn(linked_dsocket);
}
}
}
diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc
index fb34144abf6..aa23777b664 100644
--- a/source/blender/nodes/intern/math_functions.cc
+++ b/source/blender/nodes/intern/math_functions.cc
@@ -209,6 +209,8 @@ const FloatMathOperationInfo *get_float3_math_operation_info(const int operation
RETURN_OPERATION_INFO("Refract", "vector_math_refract");
case NODE_VECTOR_MATH_FACEFORWARD:
RETURN_OPERATION_INFO("Faceforward", "vector_math_faceforward");
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ RETURN_OPERATION_INFO("Multiply Add", "vector_math_multiply_add");
}
#undef RETURN_OPERATION_INFO
diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.c
index 0b1ab85c059..7fc9f664df0 100644
--- a/source/blender/nodes/intern/node_common.c
+++ b/source/blender/nodes/intern/node_common.c
@@ -79,12 +79,12 @@ void node_group_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int ma
BLI_strncpy(label, (node->id) ? node->id->name + 2 : IFACE_("Missing Data-Block"), maxlen);
}
-bool node_group_poll_instance(bNode *node, bNodeTree *nodetree)
+bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **disabled_hint)
{
- if (node->typeinfo->poll(node->typeinfo, nodetree)) {
+ if (node->typeinfo->poll(node->typeinfo, nodetree, disabled_hint)) {
bNodeTree *grouptree = (bNodeTree *)node->id;
if (grouptree) {
- return nodeGroupPoll(nodetree, grouptree);
+ return nodeGroupPoll(nodetree, grouptree, disabled_hint);
}
return true; /* without a linked node tree, group node is always ok */
@@ -93,25 +93,27 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree)
return false;
}
-int nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree)
+bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint)
{
bNode *node;
- int valid = 1;
+ bool valid = true;
/* unspecified node group, generally allowed
* (if anything, should be avoided on operator level)
*/
if (grouptree == NULL) {
- return 1;
+ return true;
}
if (nodetree == grouptree) {
- return 0;
+ *r_disabled_hint = "Nesting a node group inside of itself is not allowed";
+ return false;
}
for (node = grouptree->nodes.first; node; node = node->next) {
- if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, nodetree)) {
- valid = 0;
+ if (node->typeinfo->poll_instance &&
+ !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) {
+ valid = false;
break;
}
}
diff --git a/source/blender/nodes/intern/node_common.h b/source/blender/nodes/intern/node_common.h
index 7aad6782640..cdb7b6897b9 100644
--- a/source/blender/nodes/intern/node_common.h
+++ b/source/blender/nodes/intern/node_common.h
@@ -32,7 +32,9 @@ extern "C" {
struct bNodeTree;
void node_group_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen);
-bool node_group_poll_instance(struct bNode *node, struct bNodeTree *nodetree);
+bool node_group_poll_instance(struct bNode *node,
+ struct bNodeTree *nodetree,
+ const char **r_disabled_hint);
void ntree_update_reroute_nodes(struct bNodeTree *ntree);
diff --git a/source/blender/nodes/intern/node_exec.c b/source/blender/nodes/intern/node_exec.cc
index dd9d0b6796a..18403417af3 100644
--- a/source/blender/nodes/intern/node_exec.c
+++ b/source/blender/nodes/intern/node_exec.cc
@@ -47,7 +47,7 @@ bNodeStack *node_get_socket_stack(bNodeStack *stack, bNodeSocket *sock)
if (stack && sock && sock->stack_index >= 0) {
return stack + sock->stack_index;
}
- return NULL;
+ return nullptr;
}
void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out)
@@ -56,13 +56,13 @@ void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack
/* build pointer stack */
if (in) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
*(in++) = node_get_socket_stack(stack, sock);
}
}
if (out) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
*(out++) = node_get_socket_stack(stack, sock);
}
}
@@ -90,7 +90,7 @@ static void node_init_output_index(bNodeSocket *sock, int *index, ListBase *inte
if (internal_links) {
bNodeLink *link;
/* copy the stack index from internally connected input to skip the node */
- for (link = internal_links->first; link; link = link->next) {
+ for (link = (bNodeLink *)internal_links->first; link; link = link->next) {
if (link->tosock == sock) {
sock->stack_index = link->fromsock->stack_index;
/* set the link pointer to indicate that this socket
@@ -128,7 +128,7 @@ static struct bNodeStack *setup_stack(bNodeStack *stack,
{
bNodeStack *ns = node_get_socket_stack(stack, sock);
if (!ns) {
- return NULL;
+ return nullptr;
}
/* don't mess with remote socket stacks, these are initialized by other nodes! */
@@ -166,7 +166,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
int index;
bNode **nodelist;
int totnodes, n;
- /* XXX texnodes have threading issues with muting, have to disable it there ... */
+ /* XXX: texture-nodes have threading issues with muting, have to disable it there. */
/* ensure all sock->link pointers and node levels are correct */
/* Using global main here is likely totally wrong, not sure what to do about that one though...
@@ -178,7 +178,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
ntreeGetDependencyList(ntree, &nodelist, &totnodes);
/* XXX could let callbacks do this for specialized data */
- exec = MEM_callocN(sizeof(bNodeTreeExec), "node tree execution data");
+ exec = (bNodeTreeExec *)MEM_callocN(sizeof(bNodeTreeExec), "node tree execution data");
/* backpointer to node tree */
exec->nodetree = ntree;
@@ -188,28 +188,29 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
node = nodelist[n];
/* init node socket stack indexes */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
node_init_input_index(sock, &index);
}
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
node_init_output_index(sock, &index, &node->internal_links);
}
}
else {
- for (sock = node->outputs.first; sock; sock = sock->next) {
- node_init_output_index(sock, &index, NULL);
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
+ node_init_output_index(sock, &index, nullptr);
}
}
}
/* allocated exec data pointers for nodes */
exec->totnodes = totnodes;
- exec->nodeexec = MEM_callocN(exec->totnodes * sizeof(bNodeExec), "node execution data");
+ exec->nodeexec = (bNodeExec *)MEM_callocN(exec->totnodes * sizeof(bNodeExec),
+ "node execution data");
/* allocate data pointer for node stack */
exec->stacksize = index;
- exec->stack = MEM_callocN(exec->stacksize * sizeof(bNodeStack), "bNodeStack");
+ exec->stack = (bNodeStack *)MEM_callocN(exec->stacksize * sizeof(bNodeStack), "bNodeStack");
/* all non-const results are considered inputs */
for (n = 0; n < exec->stacksize; n++) {
@@ -222,7 +223,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
nodeexec->free_exec_fn = node->typeinfo->free_exec_fn;
/* tag inputs */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
/* disable the node if an input link is invalid */
if (sock->link && !(sock->link->flag & NODE_LINK_VALID)) {
node->need_exec = 0;
@@ -235,14 +236,14 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
}
/* tag all outputs */
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
/* ns = */ setup_stack(exec->stack, ntree, node, sock);
}
nodekey = BKE_node_instance_key(parent_key, ntree, node);
- nodeexec->data.preview = context->previews ?
- BKE_node_instance_hash_lookup(context->previews, nodekey) :
- NULL;
+ nodeexec->data.preview = context->previews ? (bNodePreview *)BKE_node_instance_hash_lookup(
+ context->previews, nodekey) :
+ nullptr;
if (node->typeinfo->init_exec_fn) {
nodeexec->data.data = node->typeinfo->init_exec_fn(context, node, nodekey);
}
@@ -284,7 +285,7 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
ListBase *lb = &exec->threadstack[thread];
bNodeThreadStack *nts;
- for (nts = lb->first; nts; nts = nts->next) {
+ for (nts = (bNodeThreadStack *)lb->first; nts; nts = nts->next) {
if (!nts->used) {
nts->used = true;
break;
@@ -292,8 +293,8 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
}
if (!nts) {
- nts = MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack");
- nts->stack = MEM_dupallocN(exec->stack);
+ nts = (bNodeThreadStack *)MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack");
+ nts->stack = (bNodeStack *)MEM_dupallocN(exec->stack);
nts->used = true;
BLI_addtail(lb, nts);
}
@@ -303,13 +304,13 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
void ntreeReleaseThreadStack(bNodeThreadStack *nts)
{
- nts->used = 0;
+ nts->used = false;
}
bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *callerdata, int thread)
{
- bNodeStack *nsin[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */
- bNodeStack *nsout[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */
+ bNodeStack *nsin[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */
+ bNodeStack *nsout[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */
bNodeExec *nodeexec;
bNode *node;
int n;
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index a4fb99a988e..188d198e159 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -22,6 +22,7 @@
#include "NOD_geometry_exec.hh"
#include "NOD_type_callbacks.hh"
+#include "NOD_type_conversions.hh"
#include "node_geometry_util.hh"
@@ -29,22 +30,22 @@ namespace blender::nodes {
void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
{
- bNodeTree *btree_cow = node_->btree();
+ bNodeTree *btree_cow = provider_->dnode->btree();
BLI_assert(btree_cow != nullptr);
if (btree_cow == nullptr) {
return;
}
bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
- const NodeTreeEvaluationContext context(*self_object_, *modifier_);
+ const NodeTreeEvaluationContext context(*provider_->self_object, *provider_->modifier);
BKE_nodetree_error_message_add(
- *btree_original, context, *node_->bnode(), type, std::move(message));
+ *btree_original, context, *provider_->dnode->bnode(), type, std::move(message));
}
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->is_available() && socket->name() == name) {
return socket->bsocket();
}
@@ -53,22 +54,29 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
return nullptr;
}
-ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const
+GVArrayPtr 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. */
+ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type);
+ const int64_t domain_size = component.attribute_domain_size(domain);
+
+ if (default_value == nullptr) {
+ default_value = cpp_type->default_value();
+ }
+
if (found_socket == nullptr) {
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
/* Try getting the attribute without the default value. */
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type);
+ GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, type);
if (attribute) {
return attribute;
}
@@ -80,25 +88,30 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
this->error_message_add(NodeWarningType::Error,
TIP_("No attribute with name \"") + name + "\"");
}
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
+ const DataTypeConversions &conversions = get_implicit_type_conversions();
if (found_socket->type == SOCK_FLOAT) {
const float value = this->get_input<float>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_FLOAT, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
if (found_socket->type == SOCK_VECTOR) {
const float3 value = this->get_input<float3>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_FLOAT3, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
if (found_socket->type == SOCK_RGBA) {
- const Color4f value = this->get_input<Color4f>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_COLOR, type, &value);
+ const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(
+ CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
BLI_assert(false);
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
@@ -114,11 +127,11 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_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;
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
+ if (info) {
+ return info->data_type;
}
- return attribute->custom_data_type();
+ return default_type;
}
if (found_socket->type == SOCK_FLOAT) {
return CD_PROP_FLOAT;
@@ -157,9 +170,9 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
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) {
- input_domains.append(attribute->domain());
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
+ if (info) {
+ input_domains.append(info->domain);
}
}
}
@@ -171,11 +184,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
return default_domain;
}
-void GeoNodeExecParams::check_extract_input(StringRef identifier,
- const CPPType *requested_type) const
+void GeoNodeExecParams::check_input_access(StringRef identifier,
+ const CPPType *requested_type) const
{
bNodeSocket *found_socket = nullptr;
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@@ -185,39 +198,39 @@ void GeoNodeExecParams::check_extract_input(StringRef identifier,
if (found_socket == nullptr) {
std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
}
std::cout << "\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
- else if (!input_values_.contains(identifier)) {
+ else if (!provider_->can_get_input(identifier)) {
std::cout << "The identifier '" << identifier
<< "' is valid, but there is no value for it anymore.\n";
std::cout << "Most likely it has been extracted before.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (requested_type != nullptr) {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
}
}
-void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const
+void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const
{
bNodeSocket *found_socket = nullptr;
- for (const OutputSocketRef *socket : node_->outputs()) {
+ for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@@ -227,29 +240,29 @@ void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &va
if (found_socket == nullptr) {
std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const OutputSocketRef *socket : node_->outputs()) {
+ for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
}
std::cout << "\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
- else if (output_values_.contains(identifier)) {
+ else if (!provider_->can_set_output(identifier)) {
std::cout << "The identifier '" << identifier << "' has been set already.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (value_type != expected_type) {
std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
}
}
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 26df3f77738..d00bf636e15 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -35,9 +35,9 @@
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
-#include "BKE_persistent_data_handle.hh"
#include "DNA_collection_types.h"
+#include "DNA_material_types.h"
#include "RNA_access.h"
#include "RNA_types.h"
@@ -52,7 +52,7 @@
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
- int in_out)
+ eNodeSocketInOut in_out)
{
bNodeSocket *sock = nodeAddStaticSocket(
ntree, node, in_out, stemp->type, stemp->subtype, stemp->identifier, stemp->name);
@@ -102,8 +102,11 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
return sock;
}
-static bNodeSocket *verify_socket_template(
- bNodeTree *ntree, bNode *node, int in_out, ListBase *socklist, bNodeSocketTemplate *stemp)
+static bNodeSocket *verify_socket_template(bNodeTree *ntree,
+ bNode *node,
+ eNodeSocketInOut in_out,
+ ListBase *socklist,
+ bNodeSocketTemplate *stemp)
{
bNodeSocket *sock;
@@ -132,7 +135,7 @@ static bNodeSocket *verify_socket_template(
static void verify_socket_template_list(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
ListBase *socklist,
bNodeSocketTemplate *stemp_first)
{
@@ -291,6 +294,21 @@ void node_socket_init_default_value(bNodeSocket *sock)
sock->default_value = dval;
break;
+ }
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *dval = (bNodeSocketValueTexture *)MEM_callocN(
+ sizeof(bNodeSocketValueTexture), "node socket value texture");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *dval = (bNodeSocketValueMaterial *)MEM_callocN(
+ sizeof(bNodeSocketValueMaterial), "node socket value material");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
break;
}
}
@@ -372,6 +390,20 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
id_us_plus(&toval->value->id);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to->default_value;
+ bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from->default_value;
+ *toval = *fromval;
+ id_us_plus(&toval->value->id);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to->default_value;
+ bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from->default_value;
+ *toval = *fromval;
+ id_us_plus(&toval->value->id);
+ break;
+ }
}
to->flag |= (from->flag & SOCK_HIDE_VALUE);
@@ -613,9 +645,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
static bNodeSocketType *make_socket_type_rgba()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::Color4f>(); };
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::ColorGeometry4f>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
- *(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
+ *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
return socktype;
}
@@ -630,63 +662,17 @@ static bNodeSocketType *make_socket_type_string()
return socktype;
}
-class ObjectSocketMultiFunction : public blender::fn::MultiFunction {
- private:
- Object *object_;
-
- public:
- ObjectSocketMultiFunction(Object *object) : object_(object)
- {
- static blender::fn::MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static blender::fn::MFSignature create_signature()
- {
- blender::fn::MFSignatureBuilder signature{"Object Socket"};
- signature.depends_on_context();
- signature.single_output<blender::bke::PersistentObjectHandle>("Object");
- return signature.build();
- }
-
- void call(blender::IndexMask mask,
- blender::fn::MFParams params,
- blender::fn::MFContext context) const override
- {
- blender::MutableSpan output =
- params.uninitialized_single_output<blender::bke::PersistentObjectHandle>(0, "Object");
-
- /* Try to get a handle map, so that the object can be converted to a handle. */
- const blender::bke::PersistentDataHandleMap *handle_map =
- context.get_global_context<blender::bke::PersistentDataHandleMap>(
- "PersistentDataHandleMap");
-
- if (handle_map == nullptr) {
- /* Return empty handles when there is no handle map. */
- output.fill_indices(mask, blender::bke::PersistentObjectHandle());
- return;
- }
-
- blender::bke::PersistentObjectHandle handle = handle_map->lookup(object_);
- for (int64_t i : mask) {
- output[i] = handle;
- }
- }
-};
-
-MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
-MAKE_CPP_TYPE(PersistentCollectionHandle, blender::bke::PersistentCollectionHandle);
+MAKE_CPP_TYPE(Object, Object *)
+MAKE_CPP_TYPE(Collection, Collection *)
+MAKE_CPP_TYPE(Texture, Tex *)
+MAKE_CPP_TYPE(Material, Material *)
static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
- socktype->get_cpp_type = []() {
- /* Objects are not passed along as raw pointers, but as handles. */
- return &blender::fn::CPPType::get<blender::bke::PersistentObjectHandle>();
- };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value;
- builder.construct_generator_fn<ObjectSocketMultiFunction>(object);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value;
};
return socktype;
}
@@ -704,9 +690,29 @@ static bNodeSocketType *make_socket_type_geometry()
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>();
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value;
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_texture()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value;
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_material()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value;
};
return socktype;
}
@@ -721,6 +727,7 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_float(PROP_FACTOR));
nodeRegisterSocketType(make_socket_type_float(PROP_ANGLE));
nodeRegisterSocketType(make_socket_type_float(PROP_TIME));
+ nodeRegisterSocketType(make_socket_type_float(PROP_TIME_ABSOLUTE));
nodeRegisterSocketType(make_socket_type_float(PROP_DISTANCE));
nodeRegisterSocketType(make_socket_type_int(PROP_NONE));
@@ -752,5 +759,9 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_collection());
+ nodeRegisterSocketType(make_socket_type_texture());
+
+ nodeRegisterSocketType(make_socket_type_material());
+
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 b973350becd..7ab6495f733 100644
--- a/source/blender/nodes/intern/node_tree_multi_function.cc
+++ b/source/blender/nodes/intern/node_tree_multi_function.cc
@@ -15,6 +15,7 @@
*/
#include "NOD_node_tree_multi_function.hh"
+#include "NOD_type_conversions.hh"
#include "FN_multi_function_network_evaluation.hh"
@@ -142,118 +143,16 @@ static void insert_nodes(CommonMFNetworkBuilderData &common)
});
}
-template<typename From, typename To>
-static void add_implicit_conversion(DataTypeConversions &conversions)
-{
- static fn::CustomMF_Convert<From, To> function;
- conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
-}
-
-template<typename From, typename To, typename ConversionF>
-static void add_implicit_conversion(DataTypeConversions &conversions,
- StringRef name,
- ConversionF conversion)
-{
- static fn::CustomMF_SI_SO<From, To> function{name, conversion};
- conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
-}
-
-static DataTypeConversions create_implicit_conversions()
-{
- DataTypeConversions conversions;
- add_implicit_conversion<float, float2>(conversions);
- add_implicit_conversion<float, float3>(conversions);
- add_implicit_conversion<float, int32_t>(conversions);
- add_implicit_conversion<float, bool>(conversions);
- add_implicit_conversion<float, Color4f>(
- conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); });
-
- add_implicit_conversion<float2, float3>(
- conversions, "float2 to float3", [](float2 a) { return float3(a.x, a.y, 0.0f); });
- add_implicit_conversion<float2, float>(
- conversions, "float2 to float", [](float2 a) { return (a.x + a.y) / 2.0f; });
- add_implicit_conversion<float2, int32_t>(
- conversions, "float2 to int32_t", [](float2 a) { return (int32_t)((a.x + a.y) / 2.0f); });
- add_implicit_conversion<float2, bool>(
- conversions, "float2 to bool", [](float2 a) { return !is_zero_v2(a); });
- add_implicit_conversion<float2, Color4f>(
- conversions, "float2 to Color4f", [](float2 a) { return Color4f(a.x, a.y, 0.0f, 1.0f); });
-
- add_implicit_conversion<float3, bool>(
- conversions, "float3 to boolean", [](float3 a) { return !is_zero_v3(a); });
- add_implicit_conversion<float3, float>(
- conversions, "float3 to float", [](float3 a) { return (a.x + a.y + a.z) / 3.0f; });
- add_implicit_conversion<float3, int32_t>(
- conversions, "float3 to int32_t", [](float3 a) { return (int)((a.x + a.y + a.z) / 3.0f); });
- add_implicit_conversion<float3, float2>(conversions);
- add_implicit_conversion<float3, Color4f>(
- conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); });
-
- add_implicit_conversion<int32_t, bool>(conversions);
- add_implicit_conversion<int32_t, float>(conversions);
- add_implicit_conversion<int32_t, float2>(
- conversions, "int32 to float2", [](int32_t a) { return float2((float)a); });
- add_implicit_conversion<int32_t, float3>(
- conversions, "int32 to float3", [](int32_t a) { return float3((float)a); });
- add_implicit_conversion<int32_t, Color4f>(conversions, "int32 to Color4f", [](int32_t a) {
- return Color4f((float)a, (float)a, (float)a, 1.0f);
- });
-
- add_implicit_conversion<bool, float>(conversions);
- add_implicit_conversion<bool, int32_t>(conversions);
- add_implicit_conversion<bool, float2>(
- conversions, "boolean to float2", [](bool a) { return (a) ? float2(1.0f) : float2(0.0f); });
- add_implicit_conversion<bool, float3>(
- conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); });
- add_implicit_conversion<bool, Color4f>(conversions, "boolean to Color4f", [](bool a) {
- return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f);
- });
-
- add_implicit_conversion<Color4f, bool>(conversions, "Color4f to boolean", [](Color4f a) {
- return a.r != 0.0f && a.g != 0.0f && a.b != 0.0f;
- });
- add_implicit_conversion<Color4f, float>(
- conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); });
- add_implicit_conversion<Color4f, float2>(
- conversions, "Color4f to float2", [](Color4f a) { return float2(a.r, a.g); });
- add_implicit_conversion<Color4f, float3>(
- conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); });
-
- return conversions;
-}
-
-const DataTypeConversions &get_implicit_type_conversions()
-{
- static const DataTypeConversions conversions = create_implicit_conversions();
- return conversions;
-}
-
-void DataTypeConversions::convert(const CPPType &from_type,
- const CPPType &to_type,
- const void *from_value,
- void *to_value) const
-{
- const fn::MultiFunction *fn = this->get_conversion(MFDataType::ForSingle(from_type),
- MFDataType::ForSingle(to_type));
- BLI_assert(fn != nullptr);
-
- fn::MFContextBuilder context;
- fn::MFParamsBuilder params{*fn, 1};
- params.add_readonly_single_input(fn::GSpan(from_type, from_value, 1));
- params.add_uninitialized_single_output(fn::GMutableSpan(to_type, to_value, 1));
- fn->call({0}, params, context);
-}
-
static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
fn::MFDataType type)
{
const fn::MultiFunction *default_fn;
if (type.is_single()) {
- default_fn = &common.resources.construct<fn::CustomMF_GenericConstant>(
+ default_fn = &common.scope.construct<fn::CustomMF_GenericConstant>(
AT, type.single_type(), type.single_type().default_value());
}
else {
- default_fn = &common.resources.construct<fn::CustomMF_GenericConstantArray>(
+ default_fn = &common.scope.construct<fn::CustomMF_GenericConstantArray>(
AT, fn::GSpan(type.vector_base_type()));
}
@@ -320,8 +219,8 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
const fn::MFDataType from_type = from_socket->data_type();
if (from_type != to_type) {
- const fn::MultiFunction *conversion_fn = get_implicit_type_conversions().get_conversion(
- from_type, to_type);
+ const fn::MultiFunction *conversion_fn =
+ get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
if (conversion_fn != nullptr) {
fn::MFNode &node = common.network.add_function(*conversion_fn);
common.network.add_link(*from_socket, node.input(0));
@@ -348,11 +247,11 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
*/
MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
MFNetworkTreeMap network_map{tree, network};
- CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+ CommonMFNetworkBuilderData common{scope, network, network_map, tree};
insert_nodes(common);
insert_links_and_unlinked_inputs(common);
@@ -426,7 +325,7 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
const DNode &dnode,
fn::MFNetwork &network,
MFNetworkTreeMap &network_map,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
for (const InputSocketRef *dsocket : dnode->inputs()) {
@@ -451,7 +350,7 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
}
}
- fn::MFNetworkEvaluator &fn_evaluator = resources.construct<fn::MFNetworkEvaluator>(
+ fn::MFNetworkEvaluator &fn_evaluator = scope.construct<fn::MFNetworkEvaluator>(
__func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs));
return fn_evaluator;
}
@@ -460,16 +359,15 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
* Returns a single multi-function for every node that supports it. This makes it easier to reuse
* the multi-function implementation of nodes in different contexts.
*/
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
- ResourceCollector &resources)
+MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope)
{
/* Build a network that nodes can insert themselves into. However, the individual nodes are not
* connected. */
- fn::MFNetwork &network = resources.construct<fn::MFNetwork>(__func__);
+ fn::MFNetwork &network = scope.construct<fn::MFNetwork>(__func__);
MFNetworkTreeMap network_map{tree, network};
MultiFunctionByNode functions_by_node;
- CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+ CommonMFNetworkBuilderData common{scope, network, network_map, tree};
tree.foreach_node([&](DNode dnode) {
const bNodeType *node_type = dnode->typeinfo();
@@ -498,7 +396,7 @@ MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
/* If a node expanded into multiple functions, a new function has to be created that
* combines those. */
const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple(
- dnode, network, network_map, resources);
+ dnode, network, network_map, scope);
functions_by_node.add_new(dnode, &fn);
break;
}
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index e627a7f4497..154ee716153 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <mutex>
+
#include "NOD_node_tree_ref.hh"
#include "BLI_dot_export.hh"
@@ -105,6 +107,7 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
}
}
+ this->create_socket_identifier_maps();
this->create_linked_socket_caches();
for (NodeRef *node : nodes_by_id_) {
@@ -163,7 +166,7 @@ void NodeTreeRef::create_linked_socket_caches()
{
for (InputSocketRef *socket : input_sockets_) {
/* Find directly linked socket based on incident links. */
- Vector<SocketRef *> directly_linked_sockets;
+ Vector<const SocketRef *> directly_linked_sockets;
for (LinkRef *link : socket->directly_linked_links_) {
directly_linked_sockets.append(link->from_);
}
@@ -171,9 +174,14 @@ void NodeTreeRef::create_linked_socket_caches()
directly_linked_sockets.as_span());
/* Find logically linked sockets. */
- Vector<SocketRef *> logically_linked_sockets;
- this->foreach_logical_origin(
- *socket, [&](OutputSocketRef &origin) { logically_linked_sockets.append(&origin); });
+ Vector<const SocketRef *> logically_linked_sockets;
+ Vector<const SocketRef *> logically_linked_skipped_sockets;
+ Vector<const InputSocketRef *> handled_sockets;
+ socket->foreach_logical_origin(
+ [&](const OutputSocketRef &origin) { logically_linked_sockets.append(&origin); },
+ [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
+ false,
+ handled_sockets);
if (logically_linked_sockets == directly_linked_sockets) {
socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
}
@@ -181,11 +189,13 @@ void NodeTreeRef::create_linked_socket_caches()
socket->logically_linked_sockets_ = allocator_.construct_array_copy(
logically_linked_sockets.as_span());
}
+ socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
+ logically_linked_skipped_sockets.as_span());
}
for (OutputSocketRef *socket : output_sockets_) {
/* Find directly linked socket based on incident links. */
- Vector<SocketRef *> directly_linked_sockets;
+ Vector<const SocketRef *> directly_linked_sockets;
for (LinkRef *link : socket->directly_linked_links_) {
directly_linked_sockets.append(link->to_);
}
@@ -193,9 +203,13 @@ void NodeTreeRef::create_linked_socket_caches()
directly_linked_sockets.as_span());
/* Find logically linked sockets. */
- Vector<SocketRef *> logically_linked_sockets;
- this->foreach_logical_target(
- *socket, [&](InputSocketRef &target) { logically_linked_sockets.append(&target); });
+ Vector<const SocketRef *> logically_linked_sockets;
+ Vector<const SocketRef *> logically_linked_skipped_sockets;
+ Vector<const OutputSocketRef *> handled_sockets;
+ socket->foreach_logical_target(
+ [&](const InputSocketRef &target) { logically_linked_sockets.append(&target); },
+ [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
+ handled_sockets);
if (logically_linked_sockets == directly_linked_sockets) {
socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
}
@@ -203,61 +217,187 @@ void NodeTreeRef::create_linked_socket_caches()
socket->logically_linked_sockets_ = allocator_.construct_array_copy(
logically_linked_sockets.as_span());
}
+ socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
+ logically_linked_skipped_sockets.as_span());
}
}
-void NodeTreeRef::foreach_logical_origin(InputSocketRef &socket,
- FunctionRef<void(OutputSocketRef &)> callback,
- bool only_follow_first_input_link)
+void InputSocketRef::foreach_logical_origin(FunctionRef<void(const OutputSocketRef &)> origin_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ bool only_follow_first_input_link,
+ Vector<const InputSocketRef *> &handled_sockets) const
{
- Span<LinkRef *> links_to_check = socket.directly_linked_links_;
+ /* Protect against loops. */
+ if (handled_sockets.contains(this)) {
+ return;
+ }
+ handled_sockets.append(this);
+
+ Span<const LinkRef *> links_to_check = this->directly_linked_links();
if (only_follow_first_input_link) {
links_to_check = links_to_check.take_front(1);
}
- for (LinkRef *link : links_to_check) {
+ for (const LinkRef *link : links_to_check) {
if (link->is_muted()) {
continue;
}
- OutputSocketRef *origin = link->from_;
- NodeRef *origin_node = origin->node_;
- if (origin_node->is_reroute_node()) {
- this->foreach_logical_origin(*origin_node->inputs_[0], callback, false);
+ const OutputSocketRef &origin = link->from();
+ const NodeRef &origin_node = origin.node();
+ if (!origin.is_available()) {
+ /* Non available sockets are ignored. */
}
- else if (origin_node->is_muted()) {
- for (InternalLinkRef *internal_link : origin_node->internal_links_) {
- if (internal_link->to_ == origin) {
- this->foreach_logical_origin(*internal_link->from_, callback, true);
+ else if (origin_node.is_reroute_node()) {
+ const InputSocketRef &reroute_input = origin_node.input(0);
+ const OutputSocketRef &reroute_output = origin_node.output(0);
+ skipped_fn.call_safe(reroute_input);
+ skipped_fn.call_safe(reroute_output);
+ reroute_input.foreach_logical_origin(origin_fn, skipped_fn, false, handled_sockets);
+ }
+ else if (origin_node.is_muted()) {
+ for (const InternalLinkRef *internal_link : origin_node.internal_links()) {
+ if (&internal_link->to() == &origin) {
+ const InputSocketRef &mute_input = internal_link->from();
+ skipped_fn.call_safe(origin);
+ skipped_fn.call_safe(mute_input);
+ mute_input.foreach_logical_origin(origin_fn, skipped_fn, true, handled_sockets);
break;
}
}
}
else {
- callback(*origin);
+ origin_fn(origin);
}
}
}
-void NodeTreeRef::foreach_logical_target(OutputSocketRef &socket,
- FunctionRef<void(InputSocketRef &)> callback)
+void OutputSocketRef::foreach_logical_target(
+ FunctionRef<void(const InputSocketRef &)> target_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ Vector<const OutputSocketRef *> &handled_sockets) const
{
- for (LinkRef *link : socket.directly_linked_links_) {
+ /* Protect against loops. */
+ if (handled_sockets.contains(this)) {
+ return;
+ }
+ handled_sockets.append(this);
+
+ for (const LinkRef *link : this->directly_linked_links()) {
if (link->is_muted()) {
continue;
}
- InputSocketRef *target = link->to_;
- NodeRef *target_node = target->node_;
- if (target_node->is_reroute_node()) {
- this->foreach_logical_target(*target_node->outputs_[0], callback);
+ const InputSocketRef &target = link->to();
+ const NodeRef &target_node = target.node();
+ if (!target.is_available()) {
+ /* Non available sockets are ignored. */
}
- else if (target_node->is_muted()) {
- for (InternalLinkRef *internal_link : target_node->internal_links_) {
- if (internal_link->from_ == target) {
- this->foreach_logical_target(*internal_link->to_, callback);
+ else if (target_node.is_reroute_node()) {
+ const OutputSocketRef &reroute_output = target_node.output(0);
+ skipped_fn.call_safe(target);
+ skipped_fn.call_safe(reroute_output);
+ reroute_output.foreach_logical_target(target_fn, skipped_fn, handled_sockets);
+ }
+ else if (target_node.is_muted()) {
+ skipped_fn.call_safe(target);
+ for (const InternalLinkRef *internal_link : target_node.internal_links()) {
+ if (&internal_link->from() == &target) {
+ /* The internal link only forwards the first incoming link. */
+ if (target.is_multi_input_socket()) {
+ if (target.directly_linked_links()[0] != link) {
+ continue;
+ }
+ }
+ const OutputSocketRef &mute_output = internal_link->to();
+ skipped_fn.call_safe(target);
+ skipped_fn.call_safe(mute_output);
+ mute_output.foreach_logical_target(target_fn, skipped_fn, handled_sockets);
}
}
}
else {
- callback(*target);
+ target_fn(target);
+ }
+ }
+}
+
+namespace {
+struct SocketByIdentifierMap {
+ SocketIndexByIdentifierMap *map = nullptr;
+ std::unique_ptr<SocketIndexByIdentifierMap> owned_map;
+};
+} // namespace
+
+static std::unique_ptr<SocketIndexByIdentifierMap> create_identifier_map(const ListBase &sockets)
+{
+ std::unique_ptr<SocketIndexByIdentifierMap> map = std::make_unique<SocketIndexByIdentifierMap>();
+ int index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &sockets, index) {
+ map->add_new(socket->identifier, index);
+ }
+ return map;
+}
+
+/* This function is not threadsafe. */
+static SocketByIdentifierMap get_or_create_identifier_map(
+ const bNode &node, const ListBase &sockets, const bNodeSocketTemplate *sockets_template)
+{
+ SocketByIdentifierMap map;
+ if (sockets_template == nullptr) {
+ if (BLI_listbase_is_empty(&sockets)) {
+ static SocketIndexByIdentifierMap empty_map;
+ map.map = &empty_map;
+ }
+ else if (node.type == NODE_REROUTE) {
+ if (&node.inputs == &sockets) {
+ static SocketIndexByIdentifierMap reroute_input_map = [] {
+ SocketIndexByIdentifierMap map;
+ map.add_new("Input", 0);
+ return map;
+ }();
+ map.map = &reroute_input_map;
+ }
+ else {
+ static SocketIndexByIdentifierMap reroute_output_map = [] {
+ SocketIndexByIdentifierMap map;
+ map.add_new("Output", 0);
+ return map;
+ }();
+ map.map = &reroute_output_map;
+ }
+ }
+ else {
+ /* The node has a dynamic amount of sockets. Therefore we need to create a new map. */
+ map.owned_map = create_identifier_map(sockets);
+ map.map = &*map.owned_map;
+ }
+ }
+ else {
+ /* Cache only one map for nodes that have the same sockets. */
+ static Map<const bNodeSocketTemplate *, std::unique_ptr<SocketIndexByIdentifierMap>> maps;
+ map.map = &*maps.lookup_or_add_cb(sockets_template,
+ [&]() { return create_identifier_map(sockets); });
+ }
+ return map;
+}
+
+void NodeTreeRef::create_socket_identifier_maps()
+{
+ /* `get_or_create_identifier_map` is not threadsafe, therefore we have to hold a lock here. */
+ static std::mutex mutex;
+ std::lock_guard lock{mutex};
+
+ for (NodeRef *node : nodes_by_id_) {
+ bNode &bnode = *node->bnode_;
+ SocketByIdentifierMap inputs_map = get_or_create_identifier_map(
+ bnode, bnode.inputs, bnode.typeinfo->inputs);
+ SocketByIdentifierMap outputs_map = get_or_create_identifier_map(
+ bnode, bnode.outputs, bnode.typeinfo->outputs);
+ node->input_index_by_identifier_ = inputs_map.map;
+ node->output_index_by_identifier_ = outputs_map.map;
+ if (inputs_map.owned_map) {
+ owned_identifier_maps_.append(std::move(inputs_map.owned_map));
+ }
+ if (outputs_map.owned_map) {
+ owned_identifier_maps_.append(std::move(outputs_map.owned_map));
}
}
}
@@ -304,6 +444,21 @@ bool NodeTreeRef::has_link_cycles() const
return false;
}
+bool NodeTreeRef::has_undefined_nodes_or_sockets() const
+{
+ for (const NodeRef *node : nodes_by_id_) {
+ if (node->is_undefined()) {
+ return true;
+ }
+ }
+ for (const SocketRef *socket : sockets_by_id_) {
+ if (socket->is_undefined()) {
+ return true;
+ }
+ }
+ return true;
+}
+
std::string NodeTreeRef::to_dot() const
{
dot::DirectedGraph digraph;
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index 00db819721c..1aec280fd2b 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -140,8 +140,8 @@ void node_math_update(bNodeTree *UNUSED(ntree), bNode *node)
switch (node->custom1) {
case NODE_MATH_WRAP:
- node_sock_label(sock2, "Min");
- node_sock_label(sock3, "Max");
+ node_sock_label(sock2, "Max");
+ node_sock_label(sock3, "Min");
break;
case NODE_MATH_MULTIPLY_ADD:
node_sock_label(sock2, "Multiplier");
@@ -239,24 +239,21 @@ void node_filter_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int m
/** \name Link Insertion
* \{ */
-/* test if two sockets are interchangeable */
-static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
+static bool node_link_socket_match(const bNodeSocket *a, const bNodeSocket *b)
{
- /* check if sockets are of the same type */
+ /* Check if sockets are of the same type. */
if (a->typeinfo != b->typeinfo) {
return false;
}
- /* tests if alphabetic prefix matches
- * this allows for imperfect matches, such as numeric suffixes,
- * like Color1/Color2
- */
+ /* Test if alphabetic prefix matches, allowing for imperfect matches, such as numeric suffixes
+ * like Color1/Color2. */
int prefix_len = 0;
- char *ca = a->name, *cb = b->name;
+ const char *ca = a->name, *cb = b->name;
for (; *ca != '\0' && *cb != '\0'; ca++, cb++) {
- /* end of common prefix? */
+ /* End of common prefix? */
if (*ca != *cb) {
- /* prefix delimited by non-alphabetic char */
+ /* Prefix delimited by non-alphabetic char. */
if (isalpha(*ca) || isalpha(*cb)) {
return false;
}
@@ -267,75 +264,73 @@ static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
return prefix_len > 0;
}
-static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
+static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
{
- bNodeLink *link;
int count = 0;
- for (link = ntree->links.first; link; link = link->next) {
- if (link->fromsock == sock) {
- count++;
- }
- if (link->tosock == sock) {
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (ELEM(socket, link->fromsock, link->tosock)) {
count++;
}
}
return count;
}
-/* Find an eligible socket for linking. */
-static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *cur)
+static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree,
+ bNode *node,
+ bNodeSocket *to_socket)
{
- bNodeSocket *first = cur->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
- bNodeSocket *sock;
-
- /* Iterate over all sockets of the target node, to find one that matches the same socket type.
- * The idea behind this is: When a user connects an input to a socket that is
- * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for
- * the link that we try to overwrite and connect that previous link to the new socket. */
- sock = cur->next ? cur->next : first; /* Wrap around the list end. */
- while (sock != cur) {
- if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
- break;
+ bNodeSocket *first = to_socket->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
+
+ /* Wrap around the list end. */
+ bNodeSocket *socket_iter = to_socket->next ? to_socket->next : first;
+ while (socket_iter != to_socket) {
+ if (!nodeSocketIsHidden(socket_iter) && node_link_socket_match(socket_iter, to_socket)) {
+ const int link_count = node_count_links(ntree, socket_iter);
+ /* Add one to account for the new link being added. */
+ if (link_count + 1 <= nodeSocketLinkLimit(socket_iter)) {
+ return socket_iter; /* Found a valid free socket we can swap to. */
+ }
}
- sock = sock->next ? sock->next : first; /* Wrap around the list end. */
+ socket_iter = socket_iter->next ? socket_iter->next : first; /* Wrap around the list end. */
}
- if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
- int link_count = node_count_links(ntree, sock);
- /* Take +1 into account since we would add a new link. */
- if (link_count + 1 <= nodeSocketLinkLimit(sock)) {
- return sock; /* Found a valid free socket we can swap to. */
- }
- }
return NULL;
}
+/**
+ * The idea behind this is: When a user connects an input to a socket that is
+ * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for
+ * the link that we try to overwrite and connect that previous link to the new socket.
+ */
void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
- bNodeSocket *sock = link->tosock;
- bNodeLink *tlink, *tlink_next;
+ bNodeSocket *socket = link->tosock;
if (node != link->tonode) {
return;
}
- for (tlink = ntree->links.first; tlink; tlink = tlink_next) {
- bNodeSocket *new_sock;
- tlink_next = tlink->next;
+ /* If we're not at the link limit of the target socket, we can skip
+ * trying to move existing links to another socket. */
+ const int to_link_limit = nodeSocketLinkLimit(socket);
+ if (socket->total_inputs + 1 < to_link_limit) {
+ return;
+ }
- if (sock != tlink->tosock) {
- continue;
- }
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, to_link, &ntree->links) {
+ if (socket == to_link->tosock) {
+ bNodeSocket *new_socket = node_find_linkable_socket(ntree, node, socket);
+ if (new_socket && new_socket != socket) {
+ /* Attempt to redirect the existing link to the new socket. */
+ to_link->tosock = new_socket;
+ return;
+ }
- new_sock = node_find_linkable_socket(ntree, node, sock);
- if (new_sock && new_sock != sock) {
- /* redirect existing link */
- tlink->tosock = new_sock;
- }
- else if (!new_sock) {
- /* no possible replacement, remove tlink */
- nodeRemLink(ntree, tlink);
- tlink = NULL;
+ if (new_socket == NULL) {
+ /* No possible replacement, remove the existing link. */
+ nodeRemLink(ntree, to_link);
+ return;
+ }
}
}
}
@@ -464,6 +459,22 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype
return -1;
}
}
+ case SOCK_TEXTURE: {
+ switch (from) {
+ case SOCK_TEXTURE:
+ return 1;
+ default:
+ return -1;
+ }
+ }
+ case SOCK_MATERIAL: {
+ switch (from) {
+ case SOCK_MATERIAL:
+ return 1;
+ default:
+ return -1;
+ }
+ }
default:
return -1;
}
diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc
index 26eeaebc6d4..5160432aea5 100644
--- a/source/blender/nodes/intern/type_callbacks.cc
+++ b/source/blender/nodes/intern/type_callbacks.cc
@@ -64,7 +64,8 @@ void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder)
}
else if (socket.typeinfo->get_cpp_value != nullptr) {
const CPPType &type = *socket_cpp_type_get(*socket.typeinfo);
- void *buffer = builder.resources().linear_allocator().allocate(type.size(), type.alignment());
+ void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(),
+ type.alignment());
socket.typeinfo->get_cpp_value(socket, buffer);
builder.set_constant_value(type, buffer);
}
diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc
new file mode 100644
index 00000000000..220e5ea9046
--- /dev/null
+++ b/source/blender/nodes/intern/type_conversions.cc
@@ -0,0 +1,349 @@
+/*
+ * 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 "NOD_type_conversions.hh"
+
+#include "FN_multi_function_builder.hh"
+
+#include "BLI_color.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+
+namespace blender::nodes {
+
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArrayPtr;
+using fn::MFDataType;
+
+template<typename From, typename To, To (*ConversionF)(const From &)>
+static void add_implicit_conversion(DataTypeConversions &conversions)
+{
+ const CPPType &from_type = CPPType::get<From>();
+ const CPPType &to_type = CPPType::get<To>();
+ const std::string conversion_name = from_type.name() + " to " + to_type.name();
+
+ static fn::CustomMF_SI_SO<From, To> multi_function{conversion_name, ConversionF};
+ static auto convert_single_to_initialized = [](const void *src, void *dst) {
+ *(To *)dst = ConversionF(*(const From *)src);
+ };
+ static auto convert_single_to_uninitialized = [](const void *src, void *dst) {
+ new (dst) To(ConversionF(*(const From *)src));
+ };
+ conversions.add(fn::MFDataType::ForSingle<From>(),
+ fn::MFDataType::ForSingle<To>(),
+ multi_function,
+ convert_single_to_initialized,
+ convert_single_to_uninitialized);
+}
+
+static float2 float_to_float2(const float &a)
+{
+ return float2(a);
+}
+static float3 float_to_float3(const float &a)
+{
+ return float3(a);
+}
+static int32_t float_to_int(const float &a)
+{
+ return (int32_t)a;
+}
+static bool float_to_bool(const float &a)
+{
+ return a > 0.0f;
+}
+static ColorGeometry4f float_to_color(const float &a)
+{
+ return ColorGeometry4f(a, a, a, 1.0f);
+}
+
+static float3 float2_to_float3(const float2 &a)
+{
+ return float3(a.x, a.y, 0.0f);
+}
+static float float2_to_float(const float2 &a)
+{
+ return (a.x + a.y) / 2.0f;
+}
+static int float2_to_int(const float2 &a)
+{
+ return (int32_t)((a.x + a.y) / 2.0f);
+}
+static bool float2_to_bool(const float2 &a)
+{
+ return !is_zero_v2(a);
+}
+static ColorGeometry4f float2_to_color(const float2 &a)
+{
+ return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f);
+}
+
+static bool float3_to_bool(const float3 &a)
+{
+ return !is_zero_v3(a);
+}
+static float float3_to_float(const float3 &a)
+{
+ return (a.x + a.y + a.z) / 3.0f;
+}
+static int float3_to_int(const float3 &a)
+{
+ return (int)((a.x + a.y + a.z) / 3.0f);
+}
+static float2 float3_to_float2(const float3 &a)
+{
+ return float2(a);
+}
+static ColorGeometry4f float3_to_color(const float3 &a)
+{
+ return ColorGeometry4f(a.x, a.y, a.z, 1.0f);
+}
+
+static bool int_to_bool(const int32_t &a)
+{
+ return a > 0;
+}
+static float int_to_float(const int32_t &a)
+{
+ return (float)a;
+}
+static float2 int_to_float2(const int32_t &a)
+{
+ return float2((float)a);
+}
+static float3 int_to_float3(const int32_t &a)
+{
+ return float3((float)a);
+}
+static ColorGeometry4f int_to_color(const int32_t &a)
+{
+ return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
+}
+
+static float bool_to_float(const bool &a)
+{
+ return (bool)a;
+}
+static int32_t bool_to_int(const bool &a)
+{
+ return (int32_t)a;
+}
+static float2 bool_to_float2(const bool &a)
+{
+ return (a) ? float2(1.0f) : float2(0.0f);
+}
+static float3 bool_to_float3(const bool &a)
+{
+ return (a) ? float3(1.0f) : float3(0.0f);
+}
+static ColorGeometry4f bool_to_color(const bool &a)
+{
+ return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+static bool color_to_bool(const ColorGeometry4f &a)
+{
+ return rgb_to_grayscale(a) > 0.0f;
+}
+static float color_to_float(const ColorGeometry4f &a)
+{
+ return rgb_to_grayscale(a);
+}
+static int32_t color_to_int(const ColorGeometry4f &a)
+{
+ return (int)rgb_to_grayscale(a);
+}
+static float2 color_to_float2(const ColorGeometry4f &a)
+{
+ return float2(a.r, a.g);
+}
+static float3 color_to_float3(const ColorGeometry4f &a)
+{
+ return float3(a.r, a.g, a.b);
+}
+
+static DataTypeConversions create_implicit_conversions()
+{
+ DataTypeConversions conversions;
+
+ add_implicit_conversion<float, float2, float_to_float2>(conversions);
+ add_implicit_conversion<float, float3, float_to_float3>(conversions);
+ add_implicit_conversion<float, int32_t, float_to_int>(conversions);
+ add_implicit_conversion<float, bool, float_to_bool>(conversions);
+ add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions);
+
+ add_implicit_conversion<float2, float3, float2_to_float3>(conversions);
+ add_implicit_conversion<float2, float, float2_to_float>(conversions);
+ add_implicit_conversion<float2, int32_t, float2_to_int>(conversions);
+ add_implicit_conversion<float2, bool, float2_to_bool>(conversions);
+ add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions);
+
+ add_implicit_conversion<float3, bool, float3_to_bool>(conversions);
+ add_implicit_conversion<float3, float, float3_to_float>(conversions);
+ add_implicit_conversion<float3, int32_t, float3_to_int>(conversions);
+ add_implicit_conversion<float3, float2, float3_to_float2>(conversions);
+ add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions);
+
+ add_implicit_conversion<int32_t, bool, int_to_bool>(conversions);
+ add_implicit_conversion<int32_t, float, int_to_float>(conversions);
+ add_implicit_conversion<int32_t, float2, int_to_float2>(conversions);
+ add_implicit_conversion<int32_t, float3, int_to_float3>(conversions);
+ add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions);
+
+ add_implicit_conversion<bool, float, bool_to_float>(conversions);
+ add_implicit_conversion<bool, int32_t, bool_to_int>(conversions);
+ add_implicit_conversion<bool, float2, bool_to_float2>(conversions);
+ add_implicit_conversion<bool, float3, bool_to_float3>(conversions);
+ add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions);
+
+ add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions);
+ add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions);
+
+ return conversions;
+}
+
+const DataTypeConversions &get_implicit_type_conversions()
+{
+ static const DataTypeConversions conversions = create_implicit_conversions();
+ return conversions;
+}
+
+void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value) const
+{
+ if (from_type == to_type) {
+ from_type.copy_to_uninitialized(from_value, to_value);
+ return;
+ }
+
+ const ConversionFunctions *functions = this->get_conversion_functions(
+ MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
+ BLI_assert(functions != nullptr);
+
+ functions->convert_single_to_uninitialized(from_value, to_value);
+}
+
+class GVArray_For_ConvertedGVArray : public GVArray {
+ private:
+ GVArrayPtr varray_;
+ const CPPType &from_type_;
+ ConversionFunctions old_to_new_conversions_;
+
+ public:
+ GVArray_For_ConvertedGVArray(GVArrayPtr varray,
+ const CPPType &to_type,
+ const DataTypeConversions &conversions)
+ : GVArray(to_type, varray->size()), varray_(std::move(varray)), from_type_(varray_->type())
+ {
+ old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+};
+
+class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArray {
+ private:
+ GVMutableArrayPtr varray_;
+ const CPPType &from_type_;
+ ConversionFunctions old_to_new_conversions_;
+ ConversionFunctions new_to_old_conversions_;
+
+ public:
+ GVMutableArray_For_ConvertedGVMutableArray(GVMutableArrayPtr varray,
+ const CPPType &to_type,
+ const DataTypeConversions &conversions)
+ : GVMutableArray(to_type, varray->size()),
+ varray_(std::move(varray)),
+ from_type_(varray_->type())
+ {
+ old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
+ new_to_old_conversions_ = *conversions.get_conversion_functions(to_type, from_type_);
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void set_by_move_impl(const int64_t index, void *value) override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ new_to_old_conversions_.convert_single_to_uninitialized(value, buffer);
+ varray_->set_by_relocate(index, buffer);
+ }
+};
+
+fn::GVArrayPtr DataTypeConversions::try_convert(fn::GVArrayPtr varray,
+ const CPPType &to_type) const
+{
+ const CPPType &from_type = varray->type();
+ if (from_type == to_type) {
+ return varray;
+ }
+ if (!this->is_convertible(from_type, to_type)) {
+ return {};
+ }
+ return std::make_unique<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this);
+}
+
+fn::GVMutableArrayPtr DataTypeConversions::try_convert(fn::GVMutableArrayPtr varray,
+ const CPPType &to_type) const
+{
+ const CPPType &from_type = varray->type();
+ if (from_type == to_type) {
+ return varray;
+ }
+ if (!this->is_convertible(from_type, to_type)) {
+ return {};
+ }
+ return std::make_unique<GVMutableArray_For_ConvertedGVMutableArray>(
+ std::move(varray), to_type, *this);
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index 3fb4d10979d..5ec982c4e7f 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -184,6 +184,12 @@ static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
return true;
}
+static bool shader_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER);
+}
+
bNodeTreeType *ntreeType_Shader;
void register_node_tree_type_sh(void)
@@ -205,6 +211,7 @@ void register_node_tree_type_sh(void)
tt->poll = shader_tree_poll;
tt->get_from_context = shader_get_from_context;
tt->validate_link = shader_validate_link;
+ tt->valid_socket_type = shader_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_ShaderNodeTree;
@@ -557,12 +564,14 @@ static bool ntree_shader_has_displacement(bNodeTree *ntree,
/* Non-cycles node is used as an output. */
return false;
}
+
if ((displacement->link != NULL) && !(displacement->link->flag & NODE_LINK_MUTED)) {
*r_node = displacement->link->fromnode;
*r_socket = displacement->link->fromsock;
*r_link = displacement->link;
+ return true;
}
- return displacement->link != NULL;
+ return false;
}
static void ntree_shader_relink_node_normal(bNodeTree *ntree,
@@ -1028,30 +1037,3 @@ void ntreeShaderEndExecTree(bNodeTreeExec *exec)
ntree->execdata = NULL;
}
}
-
-/* TODO: left over from Blender Internal, could reuse for new texture nodes. */
-bool ntreeShaderExecTree(bNodeTree *ntree, int thread)
-{
- ShaderCallData scd;
- bNodeThreadStack *nts = NULL;
- bNodeTreeExec *exec = ntree->execdata;
- int compat;
-
- /* ensure execdata is only initialized once */
- if (!exec) {
- BLI_thread_lock(LOCK_NODES);
- if (!ntree->execdata) {
- ntree->execdata = ntreeShaderBeginExecTree(ntree);
- }
- BLI_thread_unlock(LOCK_NODES);
-
- exec = ntree->execdata;
- }
-
- nts = ntreeGetThreadStack(exec, thread);
- compat = ntreeExecThreadNodes(exec, nts, &scd, thread);
- ntreeReleaseThreadStack(nts);
-
- /* if compat is zero, it has been using non-compatible nodes */
- return compat;
-}
diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c
index 1a2405e021f..abc2c7008c7 100644
--- a/source/blender/nodes/shader/node_shader_util.c
+++ b/source/blender/nodes/shader/node_shader_util.c
@@ -27,14 +27,24 @@
#include "node_exec.h"
-bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "ShaderNodeTree");
+ if (!STREQ(ntree->idname, "ShaderNodeTree")) {
+ *r_disabled_hint = "Not a shader node tree";
+ return false;
+ }
+ return true;
}
-static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool sh_fn_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a shader or geometry node tree";
+ return false;
+ }
+ return true;
}
void sh_node_type_base(
@@ -322,3 +332,17 @@ void node_shader_gpu_tex_mapping(GPUMaterial *mat,
}
}
}
+
+void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
+{
+ const float *xyz_to_rgb = IMB_colormanagement_get_xyz_to_rgb();
+ data->r[0] = xyz_to_rgb[0];
+ data->r[1] = xyz_to_rgb[3];
+ data->r[2] = xyz_to_rgb[6];
+ data->g[0] = xyz_to_rgb[1];
+ data->g[1] = xyz_to_rgb[4];
+ data->g[2] = xyz_to_rgb[7];
+ data->b[0] = xyz_to_rgb[2];
+ data->b[1] = xyz_to_rgb[5];
+ data->b[2] = xyz_to_rgb[8];
+}
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index 91454c3c982..dc44f0fa98f 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -80,7 +80,9 @@
extern "C" {
#endif
-bool sh_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool sh_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
void sh_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
void sh_fn_node_type_base(
@@ -93,6 +95,11 @@ typedef struct ShaderCallData {
int dummy;
} ShaderCallData;
+typedef struct XYZ_to_RGB /* Transposed #imbuf_xyz_to_rgb, passed as 3x vec3. */
+{
+ float r[3], g[3], b[3];
+} XYZ_to_RGB;
+
void nodestack_get_vec(float *in, short type_in, bNodeStack *ns);
void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, struct bNodeStack *ns);
@@ -111,6 +118,7 @@ void node_shader_gpu_tex_mapping(struct GPUMaterial *mat,
void ntreeExecGPUNodes(struct bNodeTreeExec *exec,
struct GPUMaterial *mat,
struct bNode *output_node);
+void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index 42299a193e2..f1d5040a292 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.c
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -47,7 +47,7 @@ static void node_shader_exec_curve_vec(void *UNUSED(data),
/* stack order input: vec */
/* stack order output: vec */
nodestack_get_vec(vec, SOCK_VECTOR, in[1]);
- BKE_curvemapping_evaluate3F(node->storage, out[0]->vec, vec);
+ BKE_curvemapping_evaluate3F((CurveMapping *)node->storage, out[0]->vec, vec);
}
static void node_shader_init_curve_vec(bNodeTree *UNUSED(ntree), bNode *node)
@@ -64,7 +64,7 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
float *array, layer;
int size;
- CurveMapping *cumap = node->storage;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_table_RGBA(cumap, &array, &size);
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
@@ -104,17 +104,65 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
GPU_uniform(ext_xyz[2]));
}
+class CurveVecFunction : public blender::fn::MultiFunction {
+ private:
+ const CurveMapping &cumap_;
+
+ public:
+ CurveVecFunction(const CurveMapping &cumap) : cumap_(cumap)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Curve Vec"};
+ signature.single_input<float>("Fac");
+ signature.single_input<blender::float3>("Vector");
+ signature.single_output<blender::float3>("Vector");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
+ const blender::VArray<blender::float3> &vec_in = params.readonly_single_input<blender::float3>(
+ 1, "Vector");
+ blender::MutableSpan<blender::float3> vec_out =
+ params.uninitialized_single_output<blender::float3>(2, "Vector");
+
+ for (int64_t i : mask) {
+ BKE_curvemapping_evaluate3F(&cumap_, vec_out[i], vec_in[i]);
+ if (fac[i] != 1.0f) {
+ interp_v3_v3v3(vec_out[i], vec_in[i], vec_out[i], fac[i]);
+ }
+ }
+ }
+};
+
+static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ bNode &bnode = builder.bnode();
+ CurveMapping *cumap = (CurveMapping *)bnode.storage;
+ BKE_curvemapping_init(cumap);
+ builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
+}
+
void register_node_type_sh_curve_vec(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0);
node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out);
node_type_init(&ntype, node_shader_init_curve_vec);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
- node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_vec);
+ node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec);
node_type_gpu(&ntype, gpu_shader_curve_vec);
+ ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network;
nodeRegisterType(&ntype);
}
@@ -145,7 +193,7 @@ static void node_shader_exec_curve_rgb(void *UNUSED(data),
/* stack order output: vec */
nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
nodestack_get_vec(vec, SOCK_VECTOR, in[1]);
- BKE_curvemapping_evaluateRGBF(node->storage, out[0]->vec, vec);
+ BKE_curvemapping_evaluateRGBF((CurveMapping *)node->storage, out[0]->vec, vec);
if (fac != 1.0f) {
interp_v3_v3v3(out[0]->vec, vec, out[0]->vec, fac);
}
@@ -166,7 +214,7 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat,
int size;
bool use_opti = true;
- CurveMapping *cumap = node->storage;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_init(cumap);
BKE_curvemapping_table_RGBA(cumap, &array, &size);
@@ -230,17 +278,65 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat,
GPU_uniform(ext_rgba[3]));
}
+class CurveRGBFunction : public blender::fn::MultiFunction {
+ private:
+ const CurveMapping &cumap_;
+
+ public:
+ CurveRGBFunction(const CurveMapping &cumap) : cumap_(cumap)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Curve RGB"};
+ signature.single_input<float>("Fac");
+ signature.single_input<blender::ColorGeometry4f>("Color");
+ signature.single_output<blender::ColorGeometry4f>("Color");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
+ const blender::VArray<blender::ColorGeometry4f> &col_in =
+ params.readonly_single_input<blender::ColorGeometry4f>(1, "Color");
+ blender::MutableSpan<blender::ColorGeometry4f> col_out =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(2, "Color");
+
+ for (int64_t i : mask) {
+ BKE_curvemapping_evaluateRGBF(&cumap_, col_out[i], col_in[i]);
+ if (fac[i] != 1.0f) {
+ interp_v3_v3v3(col_out[i], col_in[i], col_out[i], fac[i]);
+ }
+ }
+ }
+};
+
+static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ bNode &bnode = builder.bnode();
+ CurveMapping *cumap = (CurveMapping *)bnode.storage;
+ BKE_curvemapping_init(cumap);
+ builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
+}
+
void register_node_type_sh_curve_rgb(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out);
node_type_init(&ntype, node_shader_init_curve_rgb);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
- node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_rgb);
+ node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb);
node_type_gpu(&ntype, gpu_shader_curve_rgb);
+ ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index 3b4ea3d1bdf..ad7abd9d491 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -23,6 +23,8 @@
#include "node_shader_util.h"
+#include "BLI_math_base_safe.h"
+
/* **************** Map Range ******************** */
static bNodeSocketTemplate sh_node_map_range_in[] = {
{SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
@@ -88,6 +90,19 @@ static int gpu_shader_map_range(GPUMaterial *mat,
return ret;
}
+static void map_range_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps)
+{
+ signature->single_input<float>("Value");
+ signature->single_input<float>("From Min");
+ signature->single_input<float>("From Max");
+ signature->single_input<float>("To Min");
+ signature->single_input<float>("To Max");
+ if (use_steps) {
+ signature->single_input<float>("Steps");
+ }
+ signature->single_output<float>("Result");
+}
+
class MapRangeFunction : public blender::fn::MultiFunction {
private:
bool clamp_;
@@ -102,12 +117,7 @@ class MapRangeFunction : public blender::fn::MultiFunction {
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Map Range"};
- signature.single_input<float>("Value");
- signature.single_input<float>("From Min");
- signature.single_input<float>("From Max");
- signature.single_input<float>("To Min");
- signature.single_input<float>("To Max");
- signature.single_output<float>("Result");
+ map_range_signature(&signature, false);
return signature.build();
}
@@ -136,25 +146,163 @@ class MapRangeFunction : public blender::fn::MultiFunction {
}
};
+class MapRangeSteppedFunction : public blender::fn::MultiFunction {
+ private:
+ bool clamp_;
+
+ public:
+ MapRangeSteppedFunction(bool clamp) : clamp_(clamp)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Stepped"};
+ map_range_signature(&signature, true);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ const blender::VArray<float> &steps = params.readonly_single_input<float>(5, "Steps");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(6, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = safe_divide(floorf(factor * (steps[i] + 1.0f)), steps[i]);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+
+ if (clamp_) {
+ for (int64_t i : mask) {
+ results[i] = (to_min[i] > to_max[i]) ? clamp_f(results[i], to_max[i], to_min[i]) :
+ clamp_f(results[i], to_min[i], to_max[i]);
+ }
+ }
+ }
+};
+
+class MapRangeSmoothstepFunction : public blender::fn::MultiFunction {
+ public:
+ MapRangeSmoothstepFunction()
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Smoothstep"};
+ map_range_signature(&signature, false);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = std::clamp(factor, 0.0f, 1.0f);
+ factor = (3.0f - 2.0f * factor) * (factor * factor);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+ }
+};
+
+class MapRangeSmootherstepFunction : public blender::fn::MultiFunction {
+ public:
+ MapRangeSmootherstepFunction()
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Smoothstep"};
+ map_range_signature(&signature, false);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = std::clamp(factor, 0.0f, 1.0f);
+ factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+ }
+};
+
static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
bNode &bnode = builder.bnode();
bool clamp = bnode.custom1 != 0;
int interpolation_type = bnode.custom2;
- if (interpolation_type == NODE_MAP_RANGE_LINEAR) {
- static MapRangeFunction fn_with_clamp{true};
- static MapRangeFunction fn_without_clamp{false};
-
- if (clamp) {
- builder.set_matching_fn(fn_with_clamp);
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ if (clamp) {
+ static MapRangeFunction fn_with_clamp{true};
+ builder.set_matching_fn(fn_with_clamp);
+ }
+ else {
+ static MapRangeFunction fn_without_clamp{false};
+ builder.set_matching_fn(fn_without_clamp);
+ }
+ break;
}
- else {
- builder.set_matching_fn(fn_without_clamp);
+ case NODE_MAP_RANGE_STEPPED: {
+ if (clamp) {
+ static MapRangeSteppedFunction fn_stepped_with_clamp{true};
+ builder.set_matching_fn(fn_stepped_with_clamp);
+ }
+ else {
+ static MapRangeSteppedFunction fn_stepped_without_clamp{false};
+ builder.set_matching_fn(fn_stepped_without_clamp);
+ }
+ break;
}
- }
- else {
- builder.set_not_implemented();
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ static MapRangeSmoothstepFunction smoothstep;
+ builder.set_matching_fn(smoothstep);
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ static MapRangeSmootherstepFunction smootherstep;
+ builder.set_matching_fn(smootherstep);
+ break;
+ }
+ default:
+ builder.set_not_implemented();
+ break;
}
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
index 090c6216224..52f2adb10dd 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c
+++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
@@ -61,35 +61,60 @@ static void node_shader_exec_mix_rgb(void *UNUSED(data),
copy_v3_v3(out[0]->vec, col);
}
+static const char *gpu_shader_get_name(int mode)
+{
+ switch (mode) {
+ case MA_RAMP_BLEND:
+ return "mix_blend";
+ case MA_RAMP_ADD:
+ return "mix_add";
+ case MA_RAMP_MULT:
+ return "mix_mult";
+ case MA_RAMP_SUB:
+ return "mix_sub";
+ case MA_RAMP_SCREEN:
+ return "mix_screen";
+ case MA_RAMP_DIV:
+ return "mix_div";
+ case MA_RAMP_DIFF:
+ return "mix_diff";
+ case MA_RAMP_DARK:
+ return "mix_dark";
+ case MA_RAMP_LIGHT:
+ return "mix_light";
+ case MA_RAMP_OVERLAY:
+ return "mix_overlay";
+ case MA_RAMP_DODGE:
+ return "mix_dodge";
+ case MA_RAMP_BURN:
+ return "mix_burn";
+ case MA_RAMP_HUE:
+ return "mix_hue";
+ case MA_RAMP_SAT:
+ return "mix_sat";
+ case MA_RAMP_VAL:
+ return "mix_val";
+ case MA_RAMP_COLOR:
+ return "mix_color";
+ case MA_RAMP_SOFT:
+ return "mix_soft";
+ case MA_RAMP_LINEAR:
+ return "mix_linear";
+ }
+
+ return nullptr;
+}
+
static int gpu_shader_mix_rgb(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
GPUNodeStack *out)
{
- static const char *names[] = {
- "mix_blend",
- "mix_add",
- "mix_mult",
- "mix_sub",
- "mix_screen",
- "mix_div",
- "mix_diff",
- "mix_dark",
- "mix_light",
- "mix_overlay",
- "mix_dodge",
- "mix_burn",
- "mix_hue",
- "mix_sat",
- "mix_val",
- "mix_color",
- "mix_soft",
- "mix_linear",
- };
+ const char *name = gpu_shader_get_name(node->custom1);
- if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) {
- int ret = GPU_stack_link(mat, node, names[node->custom1], in, out);
+ if (name != nullptr) {
+ int ret = GPU_stack_link(mat, node, name, in, out);
if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
const float min[3] = {0.0f, 0.0f, 0.0f};
const float max[3] = {1.0f, 1.0f, 1.0f};
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
index 403b3e6d9d6..7e7e1b703f1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c
+++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
@@ -43,8 +43,9 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat,
{
GPUNodeLink *outlink;
NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage;
- /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash`. */
- unsigned int hash = BLI_hash_string(aov->name) & ~1;
+ /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and
+ * `EEVEE_renderpasses_aov_hash`. */
+ unsigned int hash = BLI_hash_string(aov->name) << 1;
GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink);
GPU_material_add_output_link_aov(mat, outlink, hash);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index 8ca4a6bab5f..a7239154633 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -70,7 +70,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Separate RGB"};
- signature.single_input<blender::Color4f>("Color");
+ signature.single_input<blender::ColorGeometry4f>("Color");
signature.single_output<float>("R");
signature.single_output<float>("G");
signature.single_output<float>("B");
@@ -81,14 +81,14 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
blender::fn::MFParams params,
blender::fn::MFContext UNUSED(context)) const override
{
- const blender::VArray<blender::Color4f> &colors =
- params.readonly_single_input<blender::Color4f>(0, "Color");
+ const blender::VArray<blender::ColorGeometry4f> &colors =
+ params.readonly_single_input<blender::ColorGeometry4f>(0, "Color");
blender::MutableSpan<float> rs = params.uninitialized_single_output<float>(1, "R");
blender::MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G");
blender::MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B");
for (int64_t i : mask) {
- blender::Color4f color = colors[i];
+ blender::ColorGeometry4f color = colors[i];
rs[i] = color.r;
gs[i] = color.g;
bs[i] = color.b;
@@ -155,8 +155,9 @@ static int gpu_shader_combrgb(GPUMaterial *mat,
static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
- static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::Color4f> fn{
- "Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }};
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{
+ "Combine RGB",
+ [](float r, float g, float b) { return blender::ColorGeometry4f(r, g, b, 1.0f); }};
builder.set_matching_fn(fn);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
index 9ef05d781bd..5dc11c4df00 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
@@ -60,11 +60,6 @@ typedef struct SkyModelPreetham {
float radiance[3];
} SkyModelPreetham;
-typedef struct XYZ_to_RGB /* transposed imbuf_xyz_to_rgb, passed as 3x vec3 */
-{
- float r[3], g[3], b[3];
-} XYZ_to_RGB;
-
static float sky_perez_function(const float *lam, float theta, float gamma)
{
float ctheta = cosf(theta);
@@ -119,20 +114,6 @@ static void sky_precompute_old(SkyModelPreetham *sunsky, const float sun_angles[
sunsky->radiance[2] /= sky_perez_function(sunsky->config_y, 0, theta);
}
-static void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
-{
- const float *xyz_to_rgb = IMB_colormangement_get_xyz_to_rgb();
- data->r[0] = xyz_to_rgb[0];
- data->r[1] = xyz_to_rgb[3];
- data->r[2] = xyz_to_rgb[6];
- data->g[0] = xyz_to_rgb[1];
- data->g[1] = xyz_to_rgb[4];
- data->g[2] = xyz_to_rgb[7];
- data->b[0] = xyz_to_rgb[2];
- data->b[1] = xyz_to_rgb[5];
- data->b[2] = xyz_to_rgb[8];
-}
-
static int node_shader_gpu_tex_sky(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 90e8161c09f..5b2eb300aac 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -140,7 +140,7 @@ class ColorBandFunction : public blender::fn::MultiFunction {
{
blender::fn::MFSignatureBuilder signature{"Color Band"};
signature.single_input<float>("Value");
- signature.single_output<blender::Color4f>("Color");
+ signature.single_output<blender::ColorGeometry4f>("Color");
signature.single_output<float>("Alpha");
return signature.build();
}
@@ -150,12 +150,12 @@ class ColorBandFunction : public blender::fn::MultiFunction {
blender::fn::MFContext UNUSED(context)) const override
{
const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
- blender::MutableSpan<blender::Color4f> colors =
- params.uninitialized_single_output<blender::Color4f>(1, "Color");
+ blender::MutableSpan<blender::ColorGeometry4f> colors =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(1, "Color");
blender::MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha");
for (int64_t i : mask) {
- blender::Color4f color;
+ blender::ColorGeometry4f color;
BKE_colorband_evaluate(&color_band_, values[i], color);
colors[i] = color;
alphas[i] = color.a;
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
index 3a9822fbc8e..419a11201aa 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -23,6 +23,8 @@
#include "node_shader_util.h"
+#include "NOD_math_functions.hh"
+
/* **************** VECTOR MATH ******************** */
static bNodeSocketTemplate sh_node_vector_math_in[] = {
{SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
@@ -92,6 +94,8 @@ static const char *gpu_shader_get_name(int mode)
return "vector_math_refract";
case NODE_VECTOR_MATH_FACEFORWARD:
return "vector_math_faceforward";
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ return "vector_math_multiply_add";
}
return nullptr;
@@ -132,8 +136,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
NODE_VECTOR_MATH_ABSOLUTE,
NODE_VECTOR_MATH_FRACTION,
NODE_VECTOR_MATH_NORMALIZE));
- nodeSetSocketAvailability(
- sockC, ELEM(node->custom1, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_FACEFORWARD));
+ nodeSetSocketAvailability(sockC,
+ ELEM(node->custom1,
+ NODE_VECTOR_MATH_WRAP,
+ NODE_VECTOR_MATH_FACEFORWARD,
+ NODE_VECTOR_MATH_MULTIPLY_ADD));
nodeSetSocketAvailability(sockScale,
ELEM(node->custom1, NODE_VECTOR_MATH_SCALE, NODE_VECTOR_MATH_REFRACT));
nodeSetSocketAvailability(sockVector,
@@ -152,6 +159,10 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
node_sock_label_clear(sockC);
node_sock_label_clear(sockScale);
switch (node->custom1) {
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ node_sock_label(sockB, "Multiplier");
+ node_sock_label(sockC, "Addend");
+ break;
case NODE_VECTOR_MATH_FACEFORWARD:
node_sock_label(sockB, "Incident");
node_sock_label(sockC, "Reference");
@@ -177,117 +188,79 @@ static const blender::fn::MultiFunction &get_multi_function(
{
using blender::float3;
- const int mode = builder.bnode().custom1;
- switch (mode) {
- case NODE_VECTOR_MATH_ADD: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Add", [](float3 a, float3 b) { return a + b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_SUBTRACT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Subtract", [](float3 a, float3 b) { return a - b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_MULTIPLY: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Multiply", [](float3 a, float3 b) { return a * b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_DIVIDE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Divide", [](float3 a, float3 b) { return float3::safe_divide(a, b); }};
- return fn;
- }
- case NODE_VECTOR_MATH_CROSS_PRODUCT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Cross Product", float3::cross_high_precision};
- return fn;
- }
- case NODE_VECTOR_MATH_PROJECT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{"Project", float3::project};
- return fn;
- }
- case NODE_VECTOR_MATH_REFLECT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Reflect", [](float3 a, float3 b) { return a.reflected(b); }};
- return fn;
- }
- case NODE_VECTOR_MATH_DOT_PRODUCT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{"Dot Product", float3::dot};
- return fn;
- }
- case NODE_VECTOR_MATH_DISTANCE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{"Distance",
- float3::distance};
- return fn;
- }
- case NODE_VECTOR_MATH_LENGTH: {
- static blender::fn::CustomMF_SI_SO<float3, float> fn{"Length",
- [](float3 a) { return a.length(); }};
- return fn;
- }
- case NODE_VECTOR_MATH_SCALE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{
- "Scale", [](float3 a, float factor) { return a * factor; }};
- return fn;
- }
- case NODE_VECTOR_MATH_NORMALIZE: {
- static blender::fn::CustomMF_SI_SO<float3, float3> fn{
- "Normalize", [](float3 a) { return a.normalized(); }};
- return fn;
- }
- case NODE_VECTOR_MATH_REFRACT: {
- static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Refract",
- [](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); }};
- return fn;
- }
- case NODE_VECTOR_MATH_FACEFORWARD: {
- static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
- "Faceforward", float3::faceforward};
- return fn;
- }
- case NODE_VECTOR_MATH_SNAP: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_FLOOR: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_CEIL: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MODULO: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_FRACTION: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_ABSOLUTE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MINIMUM: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MAXIMUM: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_WRAP: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_SINE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_COSINE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_TANGENT: {
- return builder.get_not_implemented_fn();
- }
- default:
- BLI_assert_unreachable();
- return builder.get_not_implemented_fn();
- };
+ NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1);
+
+ const blender::fn::MultiFunction *multi_fn = nullptr;
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
+ info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SO<float3, float3> fn{info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_to_fl(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SO<float3, float> fn{info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ return builder.get_not_implemented_fn();
}
static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
diff --git a/source/blender/nodes/shader/nodes/node_shader_wavelength.c b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
index 6b7e1399328..30f69557020 100644
--- a/source/blender/nodes/shader/nodes/node_shader_wavelength.c
+++ b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
@@ -30,6 +30,33 @@ static bNodeSocketTemplate sh_node_wavelength_out[] = {
{-1, ""},
};
+static int node_shader_gpu_wavelength(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const int size = CM_TABLE + 1;
+ float *data = MEM_mallocN(sizeof(float) * size * 4, "cie_xyz texture");
+
+ wavelength_to_xyz_table(data, size);
+
+ float layer;
+ GPUNodeLink *ramp_texture = GPU_color_band(mat, size, data, &layer);
+ XYZ_to_RGB xyz_to_rgb;
+ get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
+ return GPU_stack_link(mat,
+ node,
+ "node_wavelength",
+ in,
+ out,
+ ramp_texture,
+ GPU_constant(&layer),
+ GPU_uniform(xyz_to_rgb.r),
+ GPU_uniform(xyz_to_rgb.g),
+ GPU_uniform(xyz_to_rgb.b));
+}
+
/* node type definition */
void register_node_type_sh_wavelength(void)
{
@@ -40,6 +67,7 @@ void register_node_type_sh_wavelength(void)
node_type_socket_templates(&ntype, sh_node_wavelength_in, sh_node_wavelength_out);
node_type_init(&ntype, NULL);
node_type_storage(&ntype, "", NULL, NULL);
+ node_type_gpu(&ntype, node_shader_gpu_wavelength);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 48027dc847b..2ae722e3cd8 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -152,6 +152,12 @@ static void update(bNodeTree *ntree)
}
}
+static bool texture_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
+}
+
bNodeTreeType *ntreeType_Texture;
void register_node_tree_type_tex(void)
@@ -171,6 +177,7 @@ void register_node_tree_type_tex(void)
tt->local_sync = local_sync;
tt->local_merge = local_merge;
tt->get_from_context = texture_get_from_context;
+ tt->valid_socket_type = texture_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_TextureNodeTree;
diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c
index 2091a8bf10e..570b10d6e89 100644
--- a/source/blender/nodes/texture/node_texture_util.c
+++ b/source/blender/nodes/texture/node_texture_util.c
@@ -39,9 +39,15 @@
#include "node_texture_util.h"
-bool tex_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool tex_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "TextureNodeTree");
+ if (!STREQ(ntree->idname, "TextureNodeTree")) {
+ *r_disabled_hint = "Not a texture node tree";
+ return false;
+ }
+ return true;
}
void tex_node_type_base(
diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h
index 74f27ef3974..8f63a1ad07d 100644
--- a/source/blender/nodes/texture/node_texture_util.h
+++ b/source/blender/nodes/texture/node_texture_util.h
@@ -106,7 +106,9 @@ typedef struct TexDelegate {
int type;
} TexDelegate;
-bool tex_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool tex_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
void tex_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
diff --git a/source/blender/python/BPY_extern_run.h b/source/blender/python/BPY_extern_run.h
index 5f12ada4ff2..b65b5d61b9d 100644
--- a/source/blender/python/BPY_extern_run.h
+++ b/source/blender/python/BPY_extern_run.h
@@ -42,27 +42,43 @@ bool BPY_run_text(struct bContext *C,
bool BPY_run_string_exec(struct bContext *C, const char *imports[], const char *expr);
bool BPY_run_string_eval(struct bContext *C, const char *imports[], const char *expr);
+/**
+ * \note When this struct is passed in as NULL,
+ * print errors to the `stdout` and clear.
+ */
+struct BPy_RunErrInfo {
+ /** Brief text, single line (can show this in status bar for e.g.). */
+ bool use_single_line_error;
+
+ /** Report with optional prefix (when non-NULL). */
+ struct ReportList *reports;
+ const char *report_prefix;
+
+ /** Allocated exception text (assign when non-NULL). */
+ char **r_string;
+};
+
/* Run, evaluating to fixed type result. */
bool BPY_run_string_as_number(struct bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
double *r_value);
bool BPY_run_string_as_intptr(struct bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
intptr_t *r_value);
bool BPY_run_string_as_string_and_size(struct bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
char **r_value,
size_t *r_value_size);
bool BPY_run_string_as_string(struct bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
char **r_value);
#ifdef __cplusplus
diff --git a/source/blender/python/bmesh/bmesh_py_api.c b/source/blender/python/bmesh/bmesh_py_api.c
index 6dcf8bc5f1c..906f8ab702f 100644
--- a/source/blender/python/bmesh/bmesh_py_api.c
+++ b/source/blender/python/bmesh/bmesh_py_api.c
@@ -125,7 +125,7 @@ static PyObject *bpy_bm_update_edit_mesh(PyObject *UNUSED(self), PyObject *args,
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O|O&O&:update_edit_mesh",
+ "O|$O&O&:update_edit_mesh",
(char **)kwlist,
&py_me,
PyC_ParseBool,
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index 563a76ac824..638975f8fba 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1065,29 +1065,21 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(
- bpy_bmesh_from_object_doc,
- ".. method:: from_object(object, depsgraph, deform=True, cage=False, face_normals=True)\n"
- "\n"
- " Initialize this bmesh from existing object datablock (currently only meshes are "
- "supported).\n"
- "\n"
- " :arg object: The object data to load.\n"
- " :type object: :class:`Object`\n"
- " :arg deform: Apply deformation modifiers.\n"
- " :type deform: boolean\n"
- " :arg cage: Get the mesh as a deformed cage.\n"
- " :type cage: boolean\n"
- " :arg face_normals: Calculate face normals.\n"
- " :type face_normals: boolean\n"
- "\n"
- " .. deprecated:: 2.93\n"
- "\n"
- " The deform parameter is deprecated, assumed to be True, and will be removed in version "
- "3.0.\n");
+PyDoc_STRVAR(bpy_bmesh_from_object_doc,
+ ".. method:: from_object(object, depsgraph, cage=False, face_normals=True)\n"
+ "\n"
+ " Initialize this bmesh from existing object data-block (only meshes are currently "
+ "supported).\n"
+ "\n"
+ " :arg object: The object data to load.\n"
+ " :type object: :class:`Object`\n"
+ " :arg cage: Get the mesh as a deformed cage.\n"
+ " :type cage: boolean\n"
+ " :arg face_normals: Calculate face normals.\n"
+ " :type face_normals: boolean\n");
static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw)
{
- static const char *kwlist[] = {"object", "depsgraph", "deform", "cage", "face_normals", NULL};
+ static const char *kwlist[] = {"object", "depsgraph", "cage", "face_normals", NULL};
PyObject *py_object;
PyObject *py_depsgraph;
Object *ob, *ob_eval;
@@ -1095,7 +1087,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
struct Scene *scene_eval;
Mesh *me_eval;
BMesh *bm;
- bool use_deform = true;
bool use_cage = false;
bool use_fnorm = true;
const CustomData_MeshMasks data_masks = CD_MASK_BMESH;
@@ -1104,13 +1095,11 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "OO|O&O&O&:from_object",
+ "OO|$O&O&:from_object",
(char **)kwlist,
&py_object,
&py_depsgraph,
PyC_ParseBool,
- &use_deform,
- PyC_ParseBool,
&use_cage,
PyC_ParseBool,
&use_fnorm) ||
@@ -1125,13 +1114,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
return NULL;
}
- if (use_deform == false) {
- PyErr_WarnEx(PyExc_FutureWarning,
- "from_object(...): the deform parameter is deprecated, assumed to be True, and "
- "will be removed in version 3.0",
- 1);
- }
-
const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
scene_eval = DEG_get_evaluated_scene(depsgraph);
ob_eval = DEG_get_evaluated_object(depsgraph, ob);
@@ -1146,7 +1128,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
return NULL;
}
- me_eval = BKE_mesh_new_from_object(depsgraph, ob_eval, true);
+ me_eval = BKE_mesh_new_from_object(depsgraph, ob_eval, true, false);
need_free = true;
}
else {
@@ -1214,7 +1196,7 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject *
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O|O&O&i:from_mesh",
+ "O|$O&O&i:from_mesh",
(char **)kwlist,
&py_mesh,
PyC_ParseBool,
@@ -1314,7 +1296,7 @@ static PyObject *bpy_bmesh_transform(BPy_BMElem *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O!|O!:transform",
+ "O!|$O!:transform",
(char **)kwlist,
&matrix_Type,
&mat,
@@ -1376,7 +1358,7 @@ static PyObject *bpy_bmesh_calc_volume(BPy_BMElem *self, PyObject *args, PyObjec
BPY_BM_CHECK_OBJ(self);
if (!PyArg_ParseTupleAndKeywords(
- args, kw, "|O!:calc_volume", (char **)kwlist, &PyBool_Type, &is_signed)) {
+ args, kw, "|$O!:calc_volume", (char **)kwlist, &PyBool_Type, &is_signed)) {
return NULL;
}
@@ -1395,7 +1377,6 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self)
BMesh *bm;
int looptris_tot;
- int tottri;
BMLoop *(*looptris)[3];
PyObject *ret;
@@ -1408,10 +1389,10 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self)
looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
looptris = PyMem_MALLOC(sizeof(*looptris) * looptris_tot);
- BM_mesh_calc_tessellation(bm, looptris, &tottri);
+ BM_mesh_calc_tessellation(bm, looptris);
- ret = PyList_New(tottri);
- for (i = 0; i < tottri; i++) {
+ ret = PyList_New(looptris_tot);
+ for (i = 0; i < looptris_tot; i++) {
PyList_SET_ITEM(ret, i, BPy_BMLoop_Array_As_Tuple(bm, looptris[i], 3));
}
@@ -1869,7 +1850,7 @@ static PyObject *bpy_bmface_copy(BPy_BMFace *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "|O&O&:BMFace.copy",
+ "|$O&O&:BMFace.copy",
(char **)kwlist,
PyC_ParseBool,
&do_verts,
@@ -2665,7 +2646,7 @@ static PyObject *bpy_bmelemseq_sort(BPy_BMElemSeq *self, PyObject *args, PyObjec
if (args != NULL) {
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "|OO&:BMElemSeq.sort",
+ "|$OO&:BMElemSeq.sort",
(char **)kwlist,
&keyfunc,
PyC_ParseBool,
diff --git a/source/blender/python/bmesh/bmesh_py_types.h b/source/blender/python/bmesh/bmesh_py_types.h
index ce894760eff..ed307ce59a0 100644
--- a/source/blender/python/bmesh/bmesh_py_types.h
+++ b/source/blender/python/bmesh/bmesh_py_types.h
@@ -53,38 +53,45 @@ extern PyTypeObject BPy_BMIter_Type;
/* cast from _any_ bmesh type - they all have BMesh first */
typedef struct BPy_BMGeneric {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
} BPy_BMGeneric;
/* BPy_BMVert/BPy_BMEdge/BPy_BMFace/BPy_BMLoop can cast to this */
typedef struct BPy_BMElem {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMElem *ele;
} BPy_BMElem;
typedef struct BPy_BMesh {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
int flag;
} BPy_BMesh;
/* element types */
typedef struct BPy_BMVert {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMVert *v;
} BPy_BMVert;
typedef struct BPy_BMEdge {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMEdge *e;
} BPy_BMEdge;
typedef struct BPy_BMFace {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMFace *f;
} BPy_BMFace;
typedef struct BPy_BMLoop {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMLoop *l;
} BPy_BMLoop;
@@ -98,7 +105,8 @@ typedef struct BPy_BMLoop {
* - BPy_BMLoopSeq_Type
*/
typedef struct BPy_BMElemSeq {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
/* if this is a sequence on an existing element,
* loops of faces for eg.
@@ -114,7 +122,8 @@ typedef struct BPy_BMElemSeq {
} BPy_BMElemSeq;
typedef struct BPy_BMIter {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
BMIter iter;
} BPy_BMIter;
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.h b/source/blender/python/bmesh/bmesh_py_types_customdata.h
index 3173813a912..8552942f73a 100644
--- a/source/blender/python/bmesh/bmesh_py_types_customdata.h
+++ b/source/blender/python/bmesh/bmesh_py_types_customdata.h
@@ -38,20 +38,23 @@ extern PyTypeObject BPy_BMLayerItem_Type;
/* all layers for vert/edge/face/loop */
typedef struct BPy_BMLayerAccess {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
char htype;
} BPy_BMLayerAccess;
/* access different layer types deform/uv/vertexcolor */
typedef struct BPy_BMLayerCollection {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
char htype;
int type; /* customdata type - CD_XXX */
} BPy_BMLayerCollection;
/* access a specific layer directly */
typedef struct BPy_BMLayerItem {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
char htype;
int type; /* customdata type - CD_XXX */
int index; /* index of this layer type */
diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
index 127ce7db503..84267a83a44 100644
--- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
@@ -48,7 +48,8 @@
#define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
typedef struct BPy_BMLoopUV {
- PyObject_VAR_HEAD MLoopUV *data;
+ PyObject_VAR_HEAD
+ MLoopUV *data;
} BPy_BMLoopUV;
PyDoc_STRVAR(bpy_bmloopuv_uv_doc,
@@ -155,7 +156,8 @@ PyObject *BPy_BMLoopUV_CreatePyObject(struct MLoopUV *mloopuv)
#define BPy_BMVertSkin_Check(v) (Py_TYPE(v) == &BPy_BMVertSkin_Type)
typedef struct BPy_BMVertSkin {
- PyObject_VAR_HEAD MVertSkin *data;
+ PyObject_VAR_HEAD
+ MVertSkin *data;
} BPy_BMVertSkin;
PyDoc_STRVAR(bpy_bmvertskin_radius_doc,
@@ -392,7 +394,8 @@ PyObject *BPy_BMLoopColor_CreatePyObject(struct MLoopCol *mloopcol)
#define BPy_BMDeformVert_Check(v) (Py_TYPE(v) == &BPy_BMDeformVert_Type)
typedef struct BPy_BMDeformVert {
- PyObject_VAR_HEAD MDeformVert *data;
+ PyObject_VAR_HEAD
+ MDeformVert *data;
} BPy_BMDeformVert;
/* Mapping Protocols
diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.h b/source/blender/python/bmesh/bmesh_py_types_meshdata.h
index b7699b387e5..426bfcef6a0 100644
--- a/source/blender/python/bmesh/bmesh_py_types_meshdata.h
+++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.h
@@ -29,7 +29,8 @@ extern PyTypeObject BPy_BMDeformVert_Type;
#define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
typedef struct BPy_BMGenericMeshData {
- PyObject_VAR_HEAD void *data;
+ PyObject_VAR_HEAD
+ void *data;
} BPy_BMGenericMeshData;
struct MDeformVert;
diff --git a/source/blender/python/bmesh/bmesh_py_types_select.h b/source/blender/python/bmesh/bmesh_py_types_select.h
index c33aa3675c5..34ca162dd09 100644
--- a/source/blender/python/bmesh/bmesh_py_types_select.h
+++ b/source/blender/python/bmesh/bmesh_py_types_select.h
@@ -32,11 +32,13 @@ extern PyTypeObject BPy_BMEditSelIter_Type;
#define BPy_BMSelectHistoryIter_Check(v) (Py_TYPE(v) == &BPy_BMEditSelIter_Type)
typedef struct BPy_BMEditSelSeq {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
} BPy_BMEditSelSeq;
typedef struct BPy_BMEditSelIter {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMEditSelection *ese;
} BPy_BMEditSelIter;
diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c
index 22c141e0958..c1e28182c53 100644
--- a/source/blender/python/bmesh/bmesh_py_utils.c
+++ b/source/blender/python/bmesh/bmesh_py_utils.c
@@ -86,7 +86,7 @@ static PyObject *bpy_bm_utils_vert_collapse_edge(PyObject *UNUSED(self), PyObjec
bm = py_edge->bm;
- e_new = BM_vert_collapse_edge(bm, py_edge->e, py_vert->v, true, true);
+ e_new = BM_vert_collapse_edge(bm, py_edge->e, py_vert->v, true, true, true);
if (e_new) {
return BPy_BMEdge_CreatePyObject(bm, e_new);
@@ -155,7 +155,7 @@ static PyObject *bpy_bm_utils_vert_collapse_faces(PyObject *UNUSED(self), PyObje
bm = py_edge->bm;
e_new = BM_vert_collapse_faces(
- bm, py_edge->e, py_vert->v, clamp_f(fac, 0.0f, 1.0f), true, do_join_faces, true);
+ bm, py_edge->e, py_vert->v, clamp_f(fac, 0.0f, 1.0f), true, do_join_faces, true, true);
if (e_new) {
return BPy_BMEdge_CreatePyObject(bm, e_new);
@@ -450,7 +450,7 @@ static PyObject *bpy_bm_utils_face_split(PyObject *UNUSED(self), PyObject *args,
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O!O!O!|OO&O!:face_split",
+ "O!O!O!|$OO&O!:face_split",
(char **)kwlist,
&BPy_BMFace_Type,
&py_face,
diff --git a/source/blender/python/generic/bgl.h b/source/blender/python/generic/bgl.h
index ee8c293945a..4e59eab46ce 100644
--- a/source/blender/python/generic/bgl.h
+++ b/source/blender/python/generic/bgl.h
@@ -32,7 +32,8 @@ int BGL_typeSize(int type);
* For Python access to OpenGL functions requiring a pointer.
*/
typedef struct _Buffer {
- PyObject_VAR_HEAD PyObject *parent;
+ PyObject_VAR_HEAD
+ PyObject *parent;
int type; /* GL_BYTE, GL_SHORT, GL_INT, GL_FLOAT */
int ndimensions;
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index c329ea7965c..9b6ca7fcec5 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -42,6 +42,18 @@ extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
extern PyObject *pyrna_id_CreatePyObject(ID *id);
extern bool pyrna_id_CheckPyObject(PyObject *obj);
+/* Currently there is no need to expose this publicly. */
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group);
+
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type);
+static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value);
+
/* -------------------------------------------------------------------- */
/** \name Python from ID-Property (Internal Conversions)
*
@@ -756,16 +768,11 @@ static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject
static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self)
{
- BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
- iter->group = self;
- iter->mode = IDPROP_ITER_KEYS;
- iter->cur = self->prop->data.group.first;
- Py_XINCREF(iter);
- return (PyObject *)iter;
+ return BPy_IDGroup_ViewKeys_CreatePyObject(self);
}
/* for simple, non nested types this is the same as BPy_IDGroup_WrapData */
-static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
+PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
{
switch (prop->type) {
case IDP_STRING:
@@ -874,6 +881,370 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name ID-Property Group Iterator Type
+ * \{ */
+
+static PyObject *BPy_IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
+{
+ if (self->group == NULL) {
+ return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+ }
+ return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
+{
+ if (self->group != NULL) {
+ PyObject_GC_UnTrack(self);
+ }
+ Py_CLEAR(self->group);
+ PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->group);
+ return 0;
+}
+
+static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self)
+{
+ Py_CLEAR(self->group);
+ return 0;
+}
+
+static bool BPy_Group_Iter_same_size_or_raise_error(BPy_IDGroup_Iter *self)
+{
+ if (self->len_init == self->group->prop->len) {
+ return true;
+ }
+ PyErr_SetString(PyExc_RuntimeError, "IDPropertyGroup changed size during iteration");
+ return false;
+}
+
+static PyObject *BPy_Group_IterKeys_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ return PyUnicode_FromString(cur->name);
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static PyObject *BPy_Group_IterValues_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ return BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop);
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static PyObject *BPy_Group_IterItems_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ PyObject *ret = PyTuple_New(2);
+ PyTuple_SET_ITEMS(ret,
+ PyUnicode_FromString(cur->name),
+ BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
+ return ret;
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+PyTypeObject BPy_IDGroup_IterKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group Iterator. */
+static void IDGroup_Iter_init_type(void)
+{
+#define SHARED_MEMBER_SET(member, value) \
+ { \
+ k_ty->member = v_ty->member = i_ty->member = value; \
+ } \
+ ((void)0)
+
+ PyTypeObject *k_ty = &BPy_IDGroup_IterKeys_Type;
+ PyTypeObject *v_ty = &BPy_IDGroup_IterValues_Type;
+ PyTypeObject *i_ty = &BPy_IDGroup_IterItems_Type;
+
+ /* Unique members. */
+ k_ty->tp_name = "IDPropertyGroupIterKeys";
+ v_ty->tp_name = "IDPropertyGroupIterValues";
+ i_ty->tp_name = "IDPropertyGroupIterItems";
+
+ k_ty->tp_iternext = (iternextfunc)BPy_Group_IterKeys_next;
+ v_ty->tp_iternext = (iternextfunc)BPy_Group_IterValues_next;
+ i_ty->tp_iternext = (iternextfunc)BPy_Group_IterItems_next;
+
+ /* Shared members. */
+ SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_Iter));
+ SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_Iter_dealloc);
+ SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_Iter_repr);
+ SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+ SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_Iter_traverse);
+ SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_Iter_clear);
+ SHARED_MEMBER_SET(tp_iter, PyObject_SelfIter);
+
+#undef SHARED_MEMBER_SET
+}
+
+static PyObject *IDGroup_Iter_New_WithType(BPy_IDProperty *group,
+ const bool reversed,
+ PyTypeObject *type)
+{
+ BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+ BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, type);
+ iter->reversed = reversed;
+ iter->group = group;
+ if (group != NULL) {
+ Py_INCREF(group);
+ PyObject_GC_Track(iter);
+ iter->cur = (reversed ? group->prop->data.group.last : group->prop->data.group.first);
+ iter->len_init = group->prop->len;
+ }
+ else {
+ iter->cur = NULL;
+ iter->len_init = 0;
+ }
+ return (PyObject *)iter;
+}
+
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterKeys_Type);
+}
+
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterValues_Type);
+}
+
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterItems_Type);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ID-Property Group View Types (Keys/Values/Items)
+ *
+ * This view types is a thin wrapper on keys/values/items, this matches Python's `dict_view` type.
+ * The is returned by `property.keys()` and is separate from the iterator that loops over keys.
+ *
+ * There are some less common features this type could support (matching Python's `dict_view`)
+ *
+ * TODO:
+ * - Efficient contains checks for values and items which currently convert to a list first.
+ * - Missing `dict_views.isdisjoint`.
+ * - Missing `tp_as_number` (`nb_subtract`, `nb_and`, `nb_xor`, `nb_or`).
+ * \{ */
+
+static PyObject *BPy_IDGroup_View_repr(BPy_IDGroup_View *self)
+{
+ if (self->group == NULL) {
+ return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+ }
+ return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_View_dealloc(BPy_IDGroup_View *self)
+{
+ if (self->group != NULL) {
+ PyObject_GC_UnTrack(self);
+ }
+ Py_CLEAR(self->group);
+ PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_View_traverse(BPy_IDGroup_View *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->group);
+ return 0;
+}
+
+static int BPy_IDGroup_View_clear(BPy_IDGroup_View *self)
+{
+ Py_CLEAR(self->group);
+ return 0;
+}
+
+/* View Specific API's (Key/Value/Items). */
+
+static PyObject *BPy_Group_ViewKeys_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterKeys_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewValues_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterValues_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewItems_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterItems_CreatePyObject(self->group, self->reversed);
+}
+
+static Py_ssize_t BPy_Group_View_len(BPy_IDGroup_View *self)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ return self->group->prop->len;
+}
+
+static int BPy_Group_ViewKeys_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ return BPy_IDGroup_Contains(self->group, value);
+}
+
+static int BPy_Group_ViewValues_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ /* TODO: implement this without first converting to a list. */
+ PyObject *list = PySequence_List((PyObject *)self);
+ const int result = PySequence_Contains(list, value);
+ Py_DECREF(list);
+ return result;
+}
+
+static int BPy_Group_ViewItems_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ /* TODO: implement this without first converting to a list. */
+ PyObject *list = PySequence_List((PyObject *)self);
+ const int result = PySequence_Contains(list, value);
+ Py_DECREF(list);
+ return result;
+}
+
+static PySequenceMethods BPy_IDGroup_ViewKeys_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewKeys_Contains, /* sq_contains */
+};
+
+static PySequenceMethods BPy_IDGroup_ViewValues_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewValues_Contains, /* sq_contains */
+};
+
+static PySequenceMethods BPy_IDGroup_ViewItems_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewItems_Contains, /* sq_contains */
+};
+
+/* Methods. */
+
+PyDoc_STRVAR(BPy_IDGroup_View_reversed_doc,
+ "Return a reverse iterator over the ID Property keys values or items.");
+
+static PyObject *BPy_IDGroup_View_reversed(BPy_IDGroup_View *self, PyObject *UNUSED(ignored))
+{
+ BPy_IDGroup_View *result = IDGroup_View_New_WithType(self->group, Py_TYPE(self));
+ result->reversed = !self->reversed;
+ return (PyObject *)result;
+}
+
+static PyMethodDef BPy_IDGroup_View_methods[] = {
+ {"__reversed__",
+ (PyCFunction)(void (*)(void))BPy_IDGroup_View_reversed,
+ METH_NOARGS,
+ BPy_IDGroup_View_reversed_doc},
+ {NULL, NULL},
+};
+
+PyTypeObject BPy_IDGroup_ViewKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_ViewValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_ViewItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group View. */
+static void IDGroup_View_init_type(void)
+{
+ PyTypeObject *k_ty = &BPy_IDGroup_ViewKeys_Type;
+ PyTypeObject *v_ty = &BPy_IDGroup_ViewValues_Type;
+ PyTypeObject *i_ty = &BPy_IDGroup_ViewItems_Type;
+
+ /* Unique members. */
+ k_ty->tp_name = "IDPropertyGroupViewKeys";
+ v_ty->tp_name = "IDPropertyGroupViewValues";
+ i_ty->tp_name = "IDPropertyGroupViewItems";
+
+ k_ty->tp_iter = (getiterfunc)BPy_Group_ViewKeys_iter;
+ v_ty->tp_iter = (getiterfunc)BPy_Group_ViewValues_iter;
+ i_ty->tp_iter = (getiterfunc)BPy_Group_ViewItems_iter;
+
+ k_ty->tp_as_sequence = &BPy_IDGroup_ViewKeys_as_sequence;
+ v_ty->tp_as_sequence = &BPy_IDGroup_ViewValues_as_sequence;
+ i_ty->tp_as_sequence = &BPy_IDGroup_ViewItems_as_sequence;
+
+ /* Shared members. */
+#define SHARED_MEMBER_SET(member, value) \
+ { \
+ k_ty->member = v_ty->member = i_ty->member = value; \
+ } \
+ ((void)0)
+
+ SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_View));
+ SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_View_dealloc);
+ SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_View_repr);
+ SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+ SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_View_traverse);
+ SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_View_clear);
+ SHARED_MEMBER_SET(tp_methods, BPy_IDGroup_View_methods);
+
+#undef SHARED_MEMBER_SET
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name ID-Property Group Methods
* \{ */
@@ -918,25 +1289,10 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args)
return NULL;
}
- IDP_RemoveFromGroup(self->prop, idprop);
+ IDP_FreeFromGroup(self->prop, idprop);
return pyform;
}
-PyDoc_STRVAR(
- BPy_IDGroup_iter_items_doc,
- ".. method:: iteritems()\n"
- "\n"
- " Iterate through the items in the dict; behaves like dictionary method iteritems.\n");
-static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self)
-{
- BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
- iter->group = self;
- iter->mode = IDPROP_ITER_ITEMS;
- iter->cur = self->prop->data.group.first;
- Py_XINCREF(iter);
- return (PyObject *)iter;
-}
-
/* utility function */
static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
{
@@ -1021,13 +1377,37 @@ PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
return seq;
}
+PyObject *BPy_Wrap_GetKeys_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewKeys_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
+PyObject *BPy_Wrap_GetValues_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewValues_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
+PyObject *BPy_Wrap_GetItems_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewItems_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
PyDoc_STRVAR(BPy_IDGroup_keys_doc,
".. method:: keys()\n"
"\n"
" Return the keys associated with this group as a list of strings.\n");
static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self)
{
- return BPy_Wrap_GetKeys(self->prop);
+ return BPy_IDGroup_ViewKeys_CreatePyObject(self);
}
PyDoc_STRVAR(BPy_IDGroup_values_doc,
@@ -1036,16 +1416,16 @@ PyDoc_STRVAR(BPy_IDGroup_values_doc,
" Return the values associated with this group.\n");
static PyObject *BPy_IDGroup_values(BPy_IDProperty *self)
{
- return BPy_Wrap_GetValues(self->id, self->prop);
+ return BPy_IDGroup_ViewValues_CreatePyObject(self);
}
PyDoc_STRVAR(BPy_IDGroup_items_doc,
".. method:: items()\n"
"\n"
- " Return the items associated with this group.\n");
+ " Iterate through the items in the dict; behaves like dictionary method items.\n");
static PyObject *BPy_IDGroup_items(BPy_IDProperty *self)
{
- return BPy_Wrap_GetItems(self->id, self->prop);
+ return BPy_IDGroup_ViewItems_CreatePyObject(self);
}
static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
@@ -1146,7 +1526,6 @@ static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
static struct PyMethodDef BPy_IDGroup_methods[] = {
{"pop", (PyCFunction)BPy_IDGroup_pop, METH_VARARGS, BPy_IDGroup_pop_doc},
- {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc},
{"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
{"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
{"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
@@ -1676,101 +2055,59 @@ PyTypeObject BPy_IDArray_Type = {
/** \} */
/* -------------------------------------------------------------------- */
-/** \name ID-Property Group Iterator Type
+/** \name Initialize Types
* \{ */
-static PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
-{
- return PyUnicode_FromFormat("(ID Property Group Iter \"%s\")", self->group->prop->name);
-}
-
-static PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
+void IDProp_Init_Types(void)
{
+ IDGroup_Iter_init_type();
+ IDGroup_View_init_type();
- if (self->cur) {
- PyObject *ret;
- IDProperty *cur;
+ PyType_Ready(&BPy_IDGroup_Type);
+ PyType_Ready(&BPy_IDArray_Type);
- cur = self->cur;
- self->cur = self->cur->next;
+ PyType_Ready(&BPy_IDGroup_IterKeys_Type);
+ PyType_Ready(&BPy_IDGroup_IterValues_Type);
+ PyType_Ready(&BPy_IDGroup_IterItems_Type);
- if (self->mode == IDPROP_ITER_ITEMS) {
- ret = PyTuple_New(2);
- PyTuple_SET_ITEMS(ret,
- PyUnicode_FromString(cur->name),
- BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
- return ret;
- }
+ PyType_Ready(&BPy_IDGroup_ViewKeys_Type);
+ PyType_Ready(&BPy_IDGroup_ViewValues_Type);
+ PyType_Ready(&BPy_IDGroup_ViewItems_Type);
+}
- return PyUnicode_FromString(cur->name);
+/**
+ * \note `group` may be NULL, unlike most other uses of this argument.
+ * This is supported so RNA keys/values/items methods returns an iterator with the expected type:
+ * - Without having ID-properties.
+ * - Without supporting #BPy_IDProperty.prop being NULL, which would incur many more checks.
+ * Python's own dictionary-views also works this way too.
+ */
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type)
+{
+ BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+ BPy_IDGroup_View *iter = PyObject_GC_New(BPy_IDGroup_View, type);
+ iter->reversed = false;
+ iter->group = group;
+ if (group != NULL) {
+ Py_INCREF(group);
+ PyObject_GC_Track(iter);
}
-
- PyErr_SetNone(PyExc_StopIteration);
- return NULL;
+ return iter;
}
-PyTypeObject BPy_IDGroup_Iter_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- /* For printing, in format "<module>.<name>" */
- "IDPropertyGroupIter", /* char *tp_name; */
- sizeof(BPy_IDGroup_Iter), /* int tp_basicsize; */
- 0, /* tp_itemsize; For allocation */
-
- /* Methods to implement standard operations */
-
- NULL, /* destructor tp_dealloc; */
- 0, /* tp_vectorcall_offset */
- NULL, /* getattrfunc tp_getattr; */
- NULL, /* setattrfunc tp_setattr; */
- NULL, /* cmpfunc tp_compare; */
- (reprfunc)IDGroup_Iter_repr, /* reprfunc tp_repr; */
-
- /* Method suites for standard classes */
-
- NULL, /* PyNumberMethods *tp_as_number; */
- NULL, /* PySequenceMethods *tp_as_sequence; */
- NULL, /* PyMappingMethods *tp_as_mapping; */
-
- /* More standard operations (here for binary compatibility) */
-
- NULL, /* hashfunc tp_hash; */
- NULL, /* ternaryfunc tp_call; */
- NULL, /* reprfunc tp_str; */
- NULL, /* getattrofunc tp_getattro; */
- NULL, /* setattrofunc tp_setattro; */
-
- /* Functions to access object as input/output buffer */
- NULL, /* PyBufferProcs *tp_as_buffer; */
-
- /*** Flags to define presence of optional/expanded features ***/
- Py_TPFLAGS_DEFAULT, /* long tp_flags; */
-
- NULL, /* char *tp_doc; Documentation string */
- /*** Assigned meaning in release 2.0 ***/
- /* call function for all accessible objects */
- NULL, /* traverseproc tp_traverse; */
-
- /* delete references to contained objects */
- NULL, /* inquiry tp_clear; */
-
- /*** Assigned meaning in release 2.1 ***/
- /*** rich comparisons ***/
- NULL, /* richcmpfunc tp_richcompare; */
-
- /*** weak reference enabler ***/
- 0, /* long tp_weaklistoffset; */
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group)
+{
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewKeys_Type);
+}
- /*** Added in release 2.2 ***/
- /* Iterators */
- PyObject_SelfIter, /* getiterfunc tp_iter; */
- (iternextfunc)BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
-};
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group)
+{
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewValues_Type);
+}
-void IDProp_Init_Types(void)
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group)
{
- PyType_Ready(&BPy_IDGroup_Type);
- PyType_Ready(&BPy_IDGroup_Iter_Type);
- PyType_Ready(&BPy_IDArray_Type);
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewItems_Type);
}
/** \} */
@@ -1801,7 +2138,15 @@ static PyObject *BPyInit_idprop_types(void)
/* bmesh_py_types.c */
PyModule_AddType(submodule, &BPy_IDGroup_Type);
- PyModule_AddType(submodule, &BPy_IDGroup_Iter_Type);
+
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewKeys_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewValues_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewItems_Type);
+
+ PyModule_AddType(submodule, &BPy_IDGroup_IterKeys_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_IterValues_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_IterItems_Type);
+
PyModule_AddType(submodule, &BPy_IDArray_Type);
return submodule;
diff --git a/source/blender/python/generic/idprop_py_api.h b/source/blender/python/generic/idprop_py_api.h
index 478dc99f73d..1e8e26a3b6d 100644
--- a/source/blender/python/generic/idprop_py_api.h
+++ b/source/blender/python/generic/idprop_py_api.h
@@ -25,45 +25,80 @@ struct ID;
struct IDProperty;
extern PyTypeObject BPy_IDArray_Type;
-extern PyTypeObject BPy_IDGroup_Iter_Type;
extern PyTypeObject BPy_IDGroup_Type;
+extern PyTypeObject BPy_IDGroup_ViewKeys_Type;
+extern PyTypeObject BPy_IDGroup_ViewValues_Type;
+extern PyTypeObject BPy_IDGroup_ViewItems_Type;
+
+extern PyTypeObject BPy_IDGroup_IterKeys_Type;
+extern PyTypeObject BPy_IDGroup_IterValues_Type;
+extern PyTypeObject BPy_IDGroup_IterItems_Type;
+
#define BPy_IDArray_Check(v) (PyObject_TypeCheck(v, &BPy_IDArray_Type))
#define BPy_IDArray_CheckExact(v) (Py_TYPE(v) == &BPy_IDArray_Type)
-#define BPy_IDGroup_Iter_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Iter_Type))
-#define BPy_IDGroup_Iter_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Iter_Type)
#define BPy_IDGroup_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Type))
#define BPy_IDGroup_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Type)
+#define BPy_IDGroup_ViewKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewKeys_Type))
+#define BPy_IDGroup_ViewKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewKeys_Type)
+#define BPy_IDGroup_ViewValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewValues_Type))
+#define BPy_IDGroup_ViewValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewValues_Type)
+#define BPy_IDGroup_ViewItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewItems_Type))
+#define BPy_IDGroup_ViewItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewItems_Type)
+
+#define BPy_IDGroup_IterKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterKeys_Type))
+#define BPy_IDGroup_IterKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterKeys_Type)
+#define BPy_IDGroup_IterValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterValues_Type))
+#define BPy_IDGroup_IterValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterValues_Type)
+#define BPy_IDGroup_IterItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterItems_Type))
+#define BPy_IDGroup_IterItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterItems_Type)
+
typedef struct BPy_IDProperty {
- PyObject_VAR_HEAD struct ID *id; /* can be NULL */
- struct IDProperty *prop; /* must be second member */
+ PyObject_VAR_HEAD
+ struct ID *id; /* can be NULL */
+ struct IDProperty *prop; /* must be second member */
struct IDProperty *parent;
- PyObject *data_wrap;
} BPy_IDProperty;
typedef struct BPy_IDArray {
- PyObject_VAR_HEAD struct ID *id; /* can be NULL */
- struct IDProperty *prop; /* must be second member */
+ PyObject_VAR_HEAD
+ struct ID *id; /* can be NULL */
+ struct IDProperty *prop; /* must be second member */
} BPy_IDArray;
typedef struct BPy_IDGroup_Iter {
- PyObject_VAR_HEAD BPy_IDProperty *group;
+ PyObject_VAR_HEAD
+ BPy_IDProperty *group;
struct IDProperty *cur;
- int mode;
+ /** Use for detecting manipulation during iteration (which is not allowed). */
+ int len_init;
+ /** Iterate in the reverse direction. */
+ bool reversed;
} BPy_IDGroup_Iter;
+/** Use to implement `IDPropertyGroup.keys/values/items` */
+typedef struct BPy_IDGroup_View {
+ PyObject_VAR_HEAD
+ /** This will be NULL when accessing keys on data that has no ID properties. */
+ BPy_IDProperty *group;
+ bool reversed;
+} BPy_IDGroup_View;
+
PyObject *BPy_Wrap_GetKeys(struct IDProperty *prop);
PyObject *BPy_Wrap_GetValues(struct ID *id, struct IDProperty *prop);
PyObject *BPy_Wrap_GetItems(struct ID *id, struct IDProperty *prop);
+
+PyObject *BPy_Wrap_GetKeys_View_WithID(struct ID *id, struct IDProperty *prop);
+PyObject *BPy_Wrap_GetValues_View_WithID(struct ID *id, struct IDProperty *prop);
+PyObject *BPy_Wrap_GetItems_View_WithID(struct ID *id, struct IDProperty *prop);
+
int BPy_Wrap_SetMapItem(struct IDProperty *prop, PyObject *key, PyObject *val);
+PyObject *BPy_IDGroup_MapDataToPy(struct IDProperty *prop);
PyObject *BPy_IDGroup_WrapData(struct ID *id, struct IDProperty *prop, struct IDProperty *parent);
bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *key, struct IDProperty *group, PyObject *ob);
void IDProp_Init_Types(void);
PyObject *BPyInit_idprop(void);
-
-#define IDPROP_ITER_KEYS 0
-#define IDPROP_ITER_ITEMS 1
diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c
index 97a66bc23c0..53e22314ec4 100644
--- a/source/blender/python/generic/imbuf_py_api.c
+++ b/source/blender/python/generic/imbuf_py_api.c
@@ -50,8 +50,8 @@ static PyObject *Py_ImBuf_CreatePyObject(ImBuf *ibuf);
typedef struct Py_ImBuf {
PyObject_VAR_HEAD
- /* can be NULL */
- ImBuf *ibuf;
+ /* can be NULL */
+ ImBuf *ibuf;
} Py_ImBuf;
static int py_imbuf_valid_check(Py_ImBuf *self)
@@ -106,7 +106,7 @@ static PyObject *py_imbuf_resize(Py_ImBuf *self, PyObject *args, PyObject *kw)
struct PyC_StringEnum method = {method_items, FAST};
static const char *_keywords[] = {"size", "method", NULL};
- static _PyArg_Parser _parser = {"(ii)|O&:resize", _keywords, 0};
+ static _PyArg_Parser _parser = {"(ii)|$O&:resize", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kw, &_parser, &size[0], &size[1], PyC_ParseStringEnum, &method)) {
return NULL;
@@ -173,7 +173,15 @@ PyDoc_STRVAR(py_imbuf_copy_doc,
static PyObject *py_imbuf_copy(Py_ImBuf *self)
{
PY_IMBUF_CHECK_OBJ(self);
- return Py_ImBuf_CreatePyObject(self->ibuf);
+ ImBuf *ibuf_copy = IMB_dupImBuf(self->ibuf);
+
+ if (UNLIKELY(ibuf_copy == NULL)) {
+ PyErr_SetString(PyExc_MemoryError,
+ "ImBuf.copy(): "
+ "failed to allocate memory memory");
+ return NULL;
+ }
+ return Py_ImBuf_CreatePyObject(ibuf_copy);
}
static PyObject *py_imbuf_deepcopy(Py_ImBuf *self, PyObject *args)
@@ -502,7 +510,7 @@ static PyObject *M_imbuf_write(PyObject *UNUSED(self), PyObject *args, PyObject
const char *filepath = NULL;
static const char *_keywords[] = {"image", "filepath", NULL};
- static _PyArg_Parser _parser = {"O!|s:write", _keywords, 0};
+ static _PyArg_Parser _parser = {"O!|$s:write", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &Py_ImBuf_Type, &py_imb, &filepath)) {
return NULL;
}
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 78f5b9ba2cc..9824d5f17c4 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -733,7 +733,6 @@ PyObject *PyC_ExceptionBuffer_Simple(void)
PyErr_Restore(error_type, error_value, error_traceback);
- PyErr_Print();
PyErr_Clear();
return string_io_buf;
}
diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt
index fe5c559fcc0..1424b35a004 100644
--- a/source/blender/python/gpu/CMakeLists.txt
+++ b/source/blender/python/gpu/CMakeLists.txt
@@ -37,10 +37,12 @@ set(SRC
gpu_py_api.c
gpu_py_batch.c
gpu_py_buffer.c
+ gpu_py_capabilities.c
gpu_py_element.c
gpu_py_framebuffer.c
gpu_py_matrix.c
gpu_py_offscreen.c
+ gpu_py_platform.c
gpu_py_select.c
gpu_py_shader.c
gpu_py_state.c
@@ -54,10 +56,12 @@ set(SRC
gpu_py_api.h
gpu_py_batch.h
gpu_py_buffer.h
+ gpu_py_capabilities.h
gpu_py_element.h
gpu_py_framebuffer.h
gpu_py_matrix.h
gpu_py_offscreen.h
+ gpu_py_platform.h
gpu_py_select.h
gpu_py_shader.h
gpu_py_state.h
diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c
index 0bc18e73d0c..5119b3612f8 100644
--- a/source/blender/python/gpu/gpu_py_api.c
+++ b/source/blender/python/gpu/gpu_py_api.c
@@ -30,7 +30,9 @@
#include "../generic/python_utildefines.h"
+#include "gpu_py_capabilities.h"
#include "gpu_py_matrix.h"
+#include "gpu_py_platform.h"
#include "gpu_py_select.h"
#include "gpu_py_state.h"
#include "gpu_py_types.h"
@@ -61,9 +63,15 @@ PyObject *BPyInit_gpu(void)
PyModule_AddObject(mod, "types", (submodule = bpygpu_types_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ PyModule_AddObject(mod, "capabilities", (submodule = bpygpu_capabilities_init()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+
PyModule_AddObject(mod, "matrix", (submodule = bpygpu_matrix_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ PyModule_AddObject(mod, "platform", (submodule = bpygpu_platform_init()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+
PyModule_AddObject(mod, "select", (submodule = bpygpu_select_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
diff --git a/source/blender/python/gpu/gpu_py_batch.h b/source/blender/python/gpu/gpu_py_batch.h
index 7c882eab8fc..84332ab4ca6 100644
--- a/source/blender/python/gpu/gpu_py_batch.h
+++ b/source/blender/python/gpu/gpu_py_batch.h
@@ -30,8 +30,8 @@ extern PyTypeObject BPyGPUBatch_Type;
typedef struct BPyGPUBatch {
PyObject_VAR_HEAD
- /* The batch is owned, we may support thin wrapped batches later. */
- struct GPUBatch *batch;
+ /* The batch is owned, we may support thin wrapped batches later. */
+ struct GPUBatch *batch;
#ifdef USE_GPU_PY_REFERENCES
/* Just to keep a user to prevent freeing buf's we're using */
PyObject *references;
diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c
index d0965e83e33..e36e3b42617 100644
--- a/source/blender/python/gpu/gpu_py_buffer.c
+++ b/source/blender/python/gpu/gpu_py_buffer.c
@@ -37,17 +37,90 @@
#include "gpu_py_buffer.h"
-// #define PYGPU_BUFFER_PROTOCOL
+//#define PYGPU_BUFFER_PROTOCOL
+#define MAX_DIMENSIONS 64
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
-static bool pygpu_buffer_dimensions_compare(int ndim,
- const Py_ssize_t *shape_a,
- const Py_ssize_t *shape_b)
+static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len)
+{
+ Py_ssize_t tot = shape[0];
+ for (int i = 1; i < shape_len; i++) {
+ tot *= shape[i];
+ }
+
+ return tot;
+}
+
+static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a,
+ const Py_ssize_t shape_a_len,
+ const Py_ssize_t *shape_b,
+ const Py_ssize_t shape_b_len)
+{
+ if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) !=
+ pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len)) {
+ PyErr_Format(PyExc_BufferError, "array size does not match");
+ return false;
+ }
+
+ return true;
+}
+
+static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
+ Py_ssize_t r_shape[MAX_DIMENSIONS],
+ Py_ssize_t *r_shape_len)
{
- return (bool)memcmp(shape_a, shape_b, ndim * sizeof(Py_ssize_t));
+ Py_ssize_t shape_len = 0;
+ if (PyLong_Check(shape_obj)) {
+ shape_len = 1;
+ if (((r_shape[0] = PyLong_AsLong(shape_obj)) < 1)) {
+ PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
+ return false;
+ }
+ }
+ else if (PySequence_Check(shape_obj)) {
+ shape_len = PySequence_Size(shape_obj);
+ if (shape_len > MAX_DIMENSIONS) {
+ PyErr_SetString(PyExc_AttributeError,
+ "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
+ return false;
+ }
+ if (shape_len < 1) {
+ PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
+ return false;
+ }
+
+ for (int i = 0; i < shape_len; i++) {
+ PyObject *ob = PySequence_GetItem(shape_obj, i);
+ if (!PyLong_Check(ob)) {
+ PyErr_Format(PyExc_TypeError,
+ "invalid dimension %i, expected an int, not a %.200s",
+ i,
+ Py_TYPE(ob)->tp_name);
+ Py_DECREF(ob);
+ return false;
+ }
+
+ r_shape[i] = PyLong_AsLong(ob);
+ Py_DECREF(ob);
+
+ if (r_shape[i] < 1) {
+ PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
+ return false;
+ }
+ }
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "invalid second argument argument expected a sequence "
+ "or an int, not a %.200s",
+ Py_TYPE(shape_obj)->tp_name);
+ }
+
+ *r_shape_len = shape_len;
+ return true;
}
static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format)
@@ -174,7 +247,7 @@ static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self)
return list;
}
-static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
+static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void *UNUSED(arg))
{
PyObject *list = PyList_New(self->shape_len);
int i;
@@ -186,6 +259,30 @@ static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
return list;
}
+static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void *UNUSED(type))
+{
+ Py_ssize_t shape[MAX_DIMENSIONS];
+ Py_ssize_t shape_len = 0;
+
+ if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) {
+ return -1;
+ }
+
+ if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) {
+ return -1;
+ }
+
+ size_t size = shape_len * sizeof(*self->shape);
+ if (shape_len != self->shape_len) {
+ MEM_freeN(self->shape);
+ self->shape = MEM_mallocN(size, __func__);
+ }
+
+ self->shape_len = shape_len;
+ memcpy(self->shape, shape, size);
+ return 0;
+}
+
static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg)
{
Py_VISIT(self->parent);
@@ -280,14 +377,13 @@ static int pygpu_buffer_ass_slice(BPyGPUBuffer *self,
return err;
}
-#define MAX_DIMENSIONS 64
static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
PyObject *length_ob, *init = NULL;
BPyGPUBuffer *buffer = NULL;
Py_ssize_t shape[MAX_DIMENSIONS];
- Py_ssize_t i, shape_len = 0;
+ Py_ssize_t shape_len = 0;
if (kwds && PyDict_Size(kwds)) {
PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args");
@@ -300,49 +396,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (PyLong_Check(length_ob)) {
- shape_len = 1;
- if (((shape[0] = PyLong_AsLong(length_ob)) < 1)) {
- PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
- return NULL;
- }
- }
- else if (PySequence_Check(length_ob)) {
- shape_len = PySequence_Size(length_ob);
- if (shape_len > MAX_DIMENSIONS) {
- PyErr_SetString(PyExc_AttributeError,
- "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
- return NULL;
- }
- if (shape_len < 1) {
- PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
- return NULL;
- }
-
- for (i = 0; i < shape_len; i++) {
- PyObject *ob = PySequence_GetItem(length_ob, i);
- if (!PyLong_Check(ob)) {
- PyErr_Format(PyExc_TypeError,
- "invalid dimension %i, expected an int, not a %.200s",
- i,
- Py_TYPE(ob)->tp_name);
- Py_DECREF(ob);
- return NULL;
- }
- shape[i] = PyLong_AsLong(ob);
- Py_DECREF(ob);
-
- if (shape[i] < 1) {
- PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
- return NULL;
- }
- }
- }
- else {
- PyErr_Format(PyExc_TypeError,
- "invalid second argument argument expected a sequence "
- "or an int, not a %.200s",
- Py_TYPE(length_ob)->tp_name);
+ if (!pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len)) {
return NULL;
}
@@ -354,11 +408,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (shape_len != pybuffer.ndim ||
- !pygpu_buffer_dimensions_compare(shape_len, shape, pybuffer.shape)) {
- PyErr_Format(PyExc_TypeError, "array size does not match");
- }
- else {
+ if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) {
buffer = pygpu_buffer_make_from_data(
init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf);
}
@@ -518,7 +568,11 @@ static PyMethodDef pygpu_buffer__tp_methods[] = {
};
static PyGetSetDef pygpu_buffer_getseters[] = {
- {"dimensions", (getter)pygpu_buffer_dimensions, NULL, NULL, NULL},
+ {"dimensions",
+ (getter)pygpu_buffer_dimensions_get,
+ (setter)pygpu_buffer_dimensions_set,
+ NULL,
+ NULL},
{NULL, NULL, NULL, NULL, NULL},
};
@@ -625,13 +679,7 @@ static size_t pygpu_buffer_calc_size(const int format,
const int shape_len,
const Py_ssize_t *shape)
{
- size_t r_size = GPU_texture_dataformat_size(format);
-
- for (int i = 0; i < shape_len; i++) {
- r_size *= shape[i];
- }
-
- return r_size;
+ return pygpu_buffer_dimensions_tot_elem(shape, shape_len) * GPU_texture_dataformat_size(format);
}
size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer)
diff --git a/source/blender/python/gpu/gpu_py_buffer.h b/source/blender/python/gpu/gpu_py_buffer.h
index 5eac5e3d309..9df22e9b780 100644
--- a/source/blender/python/gpu/gpu_py_buffer.h
+++ b/source/blender/python/gpu/gpu_py_buffer.h
@@ -30,7 +30,8 @@ extern PyTypeObject BPyGPU_BufferType;
* For Python access to GPU functions requiring a pointer.
*/
typedef struct BPyGPUBuffer {
- PyObject_VAR_HEAD PyObject *parent;
+ PyObject_VAR_HEAD
+ PyObject *parent;
int format;
int shape_len;
diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c
new file mode 100644
index 00000000000..cedce485253
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_capabilities.c
@@ -0,0 +1,148 @@
+/*
+ * 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 bpygpu
+ *
+ * - Use ``bpygpu_`` for local API.
+ * - Use ``BPyGPU`` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_capabilities.h"
+
+#include "gpu_py_capabilities.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+static PyObject *pygpu_max_texture_size_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_texture_size());
+}
+
+static PyObject *pygpu_max_texture_layers_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_texture_layers());
+}
+
+static PyObject *pygpu_max_textures_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures());
+}
+
+static PyObject *pygpu_max_textures_vert_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_vert());
+}
+
+static PyObject *pygpu_max_textures_geom_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_geom());
+}
+
+static PyObject *pygpu_max_textures_frag_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_frag());
+}
+
+static PyObject *pygpu_max_uniforms_vert_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_uniforms_vert());
+}
+
+static PyObject *pygpu_max_uniforms_frag_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_uniforms_frag());
+}
+
+static PyObject *pygpu_max_batch_indices_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_batch_indices());
+}
+
+static PyObject *pygpu_max_batch_vertices_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_batch_vertices());
+}
+
+static PyObject *pygpu_max_vertex_attribs_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_vertex_attribs());
+}
+
+static PyObject *pygpu_max_varying_floats_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_varying_floats());
+}
+
+static PyObject *pygpu_extensions_get(PyObject *UNUSED(self))
+{
+ int extensions_len = GPU_extensions_len();
+ PyObject *ret = PyTuple_New(extensions_len);
+ PyObject **ob_items = ((PyTupleObject *)ret)->ob_item;
+ for (int i = 0; i < extensions_len; i++) {
+ ob_items[i] = PyUnicode_FromString(GPU_extension_get(i));
+ }
+
+ return ret;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef pygpu_capabilities__tp_methods[] = {
+ {"max_texture_size_get", (PyCFunction)pygpu_max_texture_size_get, METH_NOARGS, NULL},
+ {"max_texture_layers_get", (PyCFunction)pygpu_max_texture_layers_get, METH_NOARGS, NULL},
+ {"max_textures_get", (PyCFunction)pygpu_max_textures_get, METH_NOARGS, NULL},
+ {"max_textures_vert_get", (PyCFunction)pygpu_max_textures_vert_get, METH_NOARGS, NULL},
+ {"max_textures_geom_get", (PyCFunction)pygpu_max_textures_geom_get, METH_NOARGS, NULL},
+ {"max_textures_frag_get", (PyCFunction)pygpu_max_textures_frag_get, METH_NOARGS, NULL},
+ {"max_uniforms_vert_get", (PyCFunction)pygpu_max_uniforms_vert_get, METH_NOARGS, NULL},
+ {"max_uniforms_frag_get", (PyCFunction)pygpu_max_uniforms_frag_get, METH_NOARGS, NULL},
+ {"max_batch_indices_get", (PyCFunction)pygpu_max_batch_indices_get, METH_NOARGS, NULL},
+ {"max_batch_vertices_get", (PyCFunction)pygpu_max_batch_vertices_get, METH_NOARGS, NULL},
+ {"max_vertex_attribs_get", (PyCFunction)pygpu_max_vertex_attribs_get, METH_NOARGS, NULL},
+ {"max_varying_floats_get", (PyCFunction)pygpu_max_varying_floats_get, METH_NOARGS, NULL},
+ {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+};
+
+PyDoc_STRVAR(pygpu_capabilities__tp_doc, "This module provides access to the GPU capabilities.");
+static PyModuleDef pygpu_capabilities_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.capabilities",
+ .m_doc = pygpu_capabilities__tp_doc,
+ .m_methods = pygpu_capabilities__tp_methods,
+};
+
+PyObject *bpygpu_capabilities_init(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&pygpu_capabilities_module_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/python/gpu/gpu_py_capabilities.h b/source/blender/python/gpu/gpu_py_capabilities.h
new file mode 100644
index 00000000000..ac138dda0c9
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_capabilities.h
@@ -0,0 +1,23 @@
+/*
+ * 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 bpygpu
+ */
+
+#pragma once
+
+PyObject *bpygpu_capabilities_init(void);
diff --git a/source/blender/python/gpu/gpu_py_element.h b/source/blender/python/gpu/gpu_py_element.h
index a8e22aae15a..1e4e905dda6 100644
--- a/source/blender/python/gpu/gpu_py_element.h
+++ b/source/blender/python/gpu/gpu_py_element.h
@@ -25,7 +25,8 @@ extern PyTypeObject BPyGPUIndexBuf_Type;
#define BPyGPUIndexBuf_Check(v) (Py_TYPE(v) == &BPyGPUIndexBuf_Type)
typedef struct BPyGPUIndexBuf {
- PyObject_VAR_HEAD struct GPUIndexBuf *elem;
+ PyObject_VAR_HEAD
+ struct GPUIndexBuf *elem;
} BPyGPUIndexBuf;
PyObject *BPyGPUIndexBuf_CreatePyObject(struct GPUIndexBuf *elem);
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c
index 34d17bb10c3..0efc0713538 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.c
+++ b/source/blender/python/gpu/gpu_py_framebuffer.c
@@ -37,6 +37,8 @@
#include "gpu_py.h"
#include "gpu_py_texture.h"
+#include "gpu_py.h"
+#include "gpu_py_buffer.h"
#include "gpu_py_framebuffer.h" /* own include */
/* -------------------------------------------------------------------- */
@@ -46,13 +48,7 @@
static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
{
if (UNLIKELY(bpygpu_fb->fb == NULL)) {
- PyErr_SetString(PyExc_ReferenceError,
-#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
- "GPU framebuffer was freed, no further access is valid"
-#else
- "GPU framebuffer: internal error"
-#endif
- );
+ PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid");
return -1;
}
return 0;
@@ -68,10 +64,6 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
{
- if (!fb) {
- return;
- }
-
if (GPU_is_init()) {
GPU_framebuffer_free(fb);
}
@@ -80,6 +72,21 @@ static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
}
}
+static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self)
+{
+ if (self->fb) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ GPU_framebuffer_py_reference_set(self->fb, NULL);
+ if (!self->shared_reference)
+#endif
+ {
+ pygpu_framebuffer_free_if_possible(self->fb);
+ }
+
+ self->fb = NULL;
+ }
+}
+
/* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */
#define GPU_PY_FRAMEBUFFER_STACK_LEN 16
@@ -316,9 +323,9 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self),
return NULL;
}
- for (int i = 1; i <= color_attachements_len; i++) {
+ for (int i = 0; i < color_attachements_len; i++) {
PyObject *o = PySequence_GetItem(color_attachements, i);
- bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i]);
+ bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i + 1]);
Py_DECREF(o);
if (!ok) {
return NULL;
@@ -336,7 +343,7 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self),
GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
- return BPyGPUFrameBuffer_CreatePyObject(fb_python);
+ return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
}
PyDoc_STRVAR(pygpu_framebuffer_is_bound_doc,
@@ -450,6 +457,100 @@ static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *U
return ret;
}
+PyDoc_STRVAR(
+ pygpu_framebuffer_read_color_doc,
+ ".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n"
+ "\n"
+ " Read a block of pixels from the frame buffer.\n"
+ "\n"
+ " :param x, y: Lower left corner of a rectangular block of pixels.\n"
+ " :param xsize, ysize: Dimensions of the pixel rectangle.\n"
+ " :type x, y, xsize, ysize: int\n"
+ " :param channels: Number of components to read.\n"
+ " :type channels: int\n"
+ " :param slot: The framebuffer slot to read data from.\n"
+ " :type slot: int\n"
+ " :param format: The format that describes the content of a single channel.\n"
+ " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
+ " :type type: str\n"
+ " :arg data: Optional Buffer object to fill with the pixels values.\n"
+ " :type data: :class:`gpu.types.Buffer`\n"
+ " :return: The Buffer with the read pixels.\n"
+ " :rtype: :class:`gpu.types.Buffer`\n");
+static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
+ int x, y, w, h, channels;
+ uint slot;
+ struct PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_RGBA8};
+ BPyGPUBuffer *py_buffer = NULL;
+
+ static const char *_keywords[] = {
+ "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL};
+ static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ &x,
+ &y,
+ &w,
+ &h,
+ &channels,
+ &slot,
+ PyC_ParseStringEnum,
+ &pygpu_dataformat,
+ &BPyGPU_BufferType,
+ &py_buffer)) {
+ return NULL;
+ }
+
+ if (!IN_RANGE_INCL(channels, 1, 4)) {
+ PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
+ return NULL;
+ }
+
+ if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
+ PyErr_SetString(PyExc_ValueError, "slot overflow");
+ return NULL;
+ }
+
+ if (py_buffer) {
+ if (pygpu_dataformat.value_found != py_buffer->format) {
+ PyErr_SetString(PyExc_AttributeError,
+ "the format of the buffer is different from that specified");
+ return NULL;
+ }
+
+ size_t size_curr = bpygpu_Buffer_size(py_buffer);
+ size_t size_expected = w * h * channels *
+ GPU_texture_dataformat_size(pygpu_dataformat.value_found);
+ if (size_curr < size_expected) {
+ PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
+ return NULL;
+ }
+ }
+ else {
+ py_buffer = BPyGPU_Buffer_CreatePyObject(
+ pygpu_dataformat.value_found, (Py_ssize_t[3]){h, w, channels}, 3, NULL);
+ BLI_assert(bpygpu_Buffer_size(py_buffer) ==
+ w * h * channels * GPU_texture_dataformat_size(pygpu_dataformat.value_found));
+ }
+
+ GPU_framebuffer_read_color(self->fb,
+ x,
+ y,
+ w,
+ h,
+ channels,
+ (int)slot,
+ pygpu_dataformat.value_found,
+ py_buffer->buf.as_void);
+
+ return (PyObject *)py_buffer;
+}
+
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
PyDoc_STRVAR(pygpu_framebuffer_free_doc,
".. method:: free()\n"
@@ -459,15 +560,14 @@ PyDoc_STRVAR(pygpu_framebuffer_free_doc,
static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
{
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
- pygpu_framebuffer_free_if_possible(self->fb);
- self->fb = NULL;
+ pygpu_framebuffer_free_safe(self);
Py_RETURN_NONE;
}
#endif
static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self)
{
- pygpu_framebuffer_free_if_possible(self->fb);
+ pygpu_framebuffer_free_safe(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
@@ -492,8 +592,12 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = {
pygpu_framebuffer_viewport_set_doc},
{"viewport_get",
(PyCFunction)pygpu_framebuffer_viewport_get,
- METH_VARARGS,
+ METH_NOARGS,
pygpu_framebuffer_viewport_get_doc},
+ {"read_color",
+ (PyCFunction)pygpu_framebuffer_read_color,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_framebuffer_read_color_doc},
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
{"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
#endif
@@ -531,13 +635,35 @@ PyTypeObject BPyGPUFrameBuffer_Type = {
/** \name Public API
* \{ */
-PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb)
+PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference)
{
BPyGPUFrameBuffer *self;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (shared_reference) {
+ void **ref = GPU_framebuffer_py_reference_get(fb);
+ if (ref) {
+ /* Retrieve BPyGPUFrameBuffer reference. */
+ self = (BPyGPUFrameBuffer *)POINTER_OFFSET(ref, -offsetof(BPyGPUFrameBuffer, fb));
+ BLI_assert(self->fb == fb);
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+ }
+#else
+ UNUSED_VARS(shared_reference);
+#endif
+
self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type);
self->fb = fb;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ self->shared_reference = shared_reference;
+
+ BLI_assert(GPU_framebuffer_py_reference_get(fb) == NULL);
+ GPU_framebuffer_py_reference_set(fb, (void **)&self->fb);
+#endif
+
return (PyObject *)self;
}
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.h b/source/blender/python/gpu/gpu_py_framebuffer.h
index 7113e7c35aa..6727328518c 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.h
+++ b/source/blender/python/gpu/gpu_py_framebuffer.h
@@ -28,6 +28,11 @@ extern PyTypeObject BPyGPUFrameBuffer_Type;
typedef struct BPyGPUFrameBuffer {
PyObject_HEAD struct GPUFrameBuffer *fb;
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ bool shared_reference;
+#endif
} BPyGPUFrameBuffer;
-PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb) ATTR_NONNULL(1);
+PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb, bool shared_reference)
+ ATTR_NONNULL(1);
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 9e9a0b9066a..0d29bc98a31 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -53,6 +53,8 @@
#include "../generic/py_capi_utils.h"
#include "gpu_py.h"
+#include "gpu_py_texture.h"
+
#include "gpu_py_offscreen.h" /* own include */
/* Define the free method to avoid breakage. */
@@ -192,7 +194,7 @@ static PyObject *pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, P
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
static const char *_keywords[] = {"restore", NULL};
- static _PyArg_Parser _parser = {"|O&:unbind", _keywords, 0};
+ static _PyArg_Parser _parser = {"|$O&:unbind", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &restore)) {
return NULL;
}
@@ -264,6 +266,17 @@ static PyObject *pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *
return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
}
+PyDoc_STRVAR(pygpu_offscreen_texture_color_doc,
+ "The color texture attached.\n"
+ "\n"
+ ":type: :class:`gpu.types.GPUTexture`");
+static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void *UNUSED(type))
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+ GPUTexture *texture = GPU_offscreen_color_texture(self->ofs);
+ return BPyGPUTexture_CreatePyObject(texture, true);
+}
+
PyDoc_STRVAR(
pygpu_offscreen_draw_view3d_doc,
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n"
@@ -385,6 +398,11 @@ static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
(setter)NULL,
pygpu_offscreen_color_texture_doc,
NULL},
+ {"texture_color",
+ (getter)pygpu_offscreen_texture_color_get,
+ (setter)NULL,
+ pygpu_offscreen_texture_color_doc,
+ NULL},
{"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
{"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c
new file mode 100644
index 00000000000..e49ad18dfd8
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_platform.c
@@ -0,0 +1,81 @@
+/*
+ * 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 bpygpu
+ *
+ * - Use ``bpygpu_`` for local API.
+ * - Use ``BPyGPU`` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_platform.h"
+
+#include "gpu_py_platform.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+static PyObject *pygpu_platform_vendor_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_vendor());
+}
+
+static PyObject *pygpu_platform_renderer_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_renderer());
+}
+
+static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_version());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef pygpu_platform__tp_methods[] = {
+ {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, NULL},
+ {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, NULL},
+ {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+};
+
+PyDoc_STRVAR(pygpu_platform__tp_doc, "This module provides access to GPU Platform definitions.");
+static PyModuleDef pygpu_platform_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.platform",
+ .m_doc = pygpu_platform__tp_doc,
+ .m_methods = pygpu_platform__tp_methods,
+};
+
+PyObject *bpygpu_platform_init(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&pygpu_platform_module_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/compositor/intern/COM_SocketReader.cc b/source/blender/python/gpu/gpu_py_platform.h
index 93c8a143b86..19e3e41fb49 100644
--- a/source/blender/compositor/intern/COM_SocketReader.cc
+++ b/source/blender/python/gpu/gpu_py_platform.h
@@ -12,8 +12,12 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2011, Blender Foundation.
*/
-#include "COM_SocketReader.h"
+/** \file
+ * \ingroup bpygpu
+ */
+
+#pragma once
+
+PyObject *bpygpu_platform_init(void);
diff --git a/source/blender/python/gpu/gpu_py_shader.h b/source/blender/python/gpu/gpu_py_shader.h
index 745b9351649..b15caa3e66c 100644
--- a/source/blender/python/gpu/gpu_py_shader.h
+++ b/source/blender/python/gpu/gpu_py_shader.h
@@ -25,7 +25,8 @@ extern PyTypeObject BPyGPUShader_Type;
#define BPyGPUShader_Check(v) (Py_TYPE(v) == &BPyGPUShader_Type)
typedef struct BPyGPUShader {
- PyObject_VAR_HEAD struct GPUShader *shader;
+ PyObject_VAR_HEAD
+ struct GPUShader *shader;
bool is_builtin;
} BPyGPUShader;
diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c
index 110f5a6ff55..173c5afba56 100644
--- a/source/blender/python/gpu/gpu_py_state.c
+++ b/source/blender/python/gpu/gpu_py_state.c
@@ -25,11 +25,13 @@
#include <Python.h>
+#include "GPU_framebuffer.h"
#include "GPU_state.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
+#include "gpu_py_framebuffer.h"
#include "gpu_py_state.h" /* own include */
/* -------------------------------------------------------------------- */
@@ -85,19 +87,20 @@ PyDoc_STRVAR(
" Defines the fixed pipeline blending equation.\n"
"\n"
" :param mode: The type of blend mode.\n"
- " * ``NONE`` No blending.\n"
- " * ``ALPHA`` The original color channels are interpolated according to the alpha value.\n"
- " * ``ALPHA_PREMULT`` The original color channels are interpolated according to the alpha "
- "value with the new colors pre-multiplied by this value.\n"
- " * ``ADDITIVE`` The original color channels are added by the corresponding ones.\n"
- " * ``ADDITIVE_PREMULT`` The original color channels are added by the corresponding ones "
+ " * ``NONE`` No blending.\n"
+ " * ``ALPHA`` The original color channels are interpolated according to the alpha "
+ "value.\n"
+ " * ``ALPHA_PREMULT`` The original color channels are interpolated according to the "
+ "alpha value with the new colors pre-multiplied by this value.\n"
+ " * ``ADDITIVE`` The original color channels are added by the corresponding ones.\n"
+ " * ``ADDITIVE_PREMULT`` The original color channels are added by the corresponding ones "
"that are pre-multiplied by the alpha value.\n"
- " * ``MULTIPLY`` The original color channels are multiplied by the corresponding ones.\n"
- " * ``SUBTRACT`` The original color channels are subtracted by the corresponding ones.\n"
- " * ``INVERT`` The original color channels are replaced by its complementary color.\n"
- //" * ``OIT``.\n"
- //" * ``BACKGROUND`` .\n"
- //" * ``CUSTOM`` .\n"
+ " * ``MULTIPLY`` The original color channels are multiplied by the corresponding ones.\n"
+ " * ``SUBTRACT`` The original color channels are subtracted by the corresponding ones.\n"
+ " * ``INVERT`` The original color channels are replaced by its complementary color.\n"
+ //" * ``OIT``.\n"
+ //" * ``BACKGROUND`` .\n"
+ //" * ``CUSTOM`` .\n"
" :type mode: str\n");
static PyObject *pygpu_state_blend_set(PyObject *UNUSED(self), PyObject *value)
{
@@ -333,6 +336,16 @@ static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyOb
Py_RETURN_NONE;
}
+PyDoc_STRVAR(pygpu_state_framebuffer_active_get_doc,
+ ".. function:: framebuffer_active_get(enable)\n"
+ "\n"
+ " Return the active framefuffer in context.\n");
+static PyObject *pygpu_state_framebuffer_active_get(PyObject *UNUSED(self))
+{
+ GPUFrameBuffer *fb = GPU_framebuffer_active_get();
+ return BPyGPUFrameBuffer_CreatePyObject(fb, true);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -395,6 +408,10 @@ static struct PyMethodDef pygpu_state__tp_methods[] = {
(PyCFunction)pygpu_state_program_point_size_set,
METH_O,
pygpu_state_program_point_size_set_doc},
+ {"active_framebuffer_get",
+ (PyCFunction)pygpu_state_framebuffer_active_get,
+ METH_NOARGS,
+ pygpu_state_framebuffer_active_get_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c
index 1ae65c1dd11..2181c09b537 100644
--- a/source/blender/python/gpu/gpu_py_texture.c
+++ b/source/blender/python/gpu/gpu_py_texture.c
@@ -247,7 +247,7 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg
return NULL;
}
- return BPyGPUTexture_CreatePyObject(tex);
+ return BPyGPUTexture_CreatePyObject(tex, false);
}
PyDoc_STRVAR(pygpu_texture_width_doc, "Width of the texture.\n\n:type: `int`");
@@ -349,11 +349,12 @@ PyDoc_STRVAR(pygpu_texture_read_doc,
static PyObject *pygpu_texture_read(BPyGPUTexture *self)
{
BPYGPU_TEXTURE_CHECK_OBJ(self);
+ eGPUTextureFormat tex_format = GPU_texture_format(self->tex);
/* #GPU_texture_read is restricted in combining 'data_format' with 'tex_format'.
* So choose data_format here. */
eGPUDataFormat best_data_format;
- switch (GPU_texture_format(self->tex)) {
+ switch (tex_format) {
case GPU_DEPTH_COMPONENT24:
case GPU_DEPTH_COMPONENT16:
case GPU_DEPTH_COMPONENT32F:
@@ -389,8 +390,12 @@ static PyObject *pygpu_texture_read(BPyGPUTexture *self)
}
void *buf = GPU_texture_read(self->tex, best_data_format, 0);
- const Py_ssize_t shape[2] = {GPU_texture_height(self->tex), GPU_texture_width(self->tex)};
- return (PyObject *)BPyGPU_Buffer_CreatePyObject(best_data_format, shape, ARRAY_SIZE(shape), buf);
+ const Py_ssize_t shape[3] = {GPU_texture_height(self->tex),
+ GPU_texture_width(self->tex),
+ GPU_texture_component_len(tex_format)};
+
+ int shape_len = (shape[2] == 1) ? 2 : 3;
+ return (PyObject *)BPyGPU_Buffer_CreatePyObject(best_data_format, shape, shape_len, buf);
}
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
@@ -412,6 +417,9 @@ static PyObject *pygpu_texture_free(BPyGPUTexture *self)
static void BPyGPUTexture__tp_dealloc(BPyGPUTexture *self)
{
if (self->tex) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ GPU_texture_py_reference_set(self->tex, NULL);
+#endif
GPU_texture_free(self->tex);
}
Py_TYPE(self)->tp_free((PyObject *)self);
@@ -535,10 +543,7 @@ static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg)
BKE_imageuser_default(&iuser);
GPUTexture *tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
- /* Increase the texture reference count. */
- GPU_texture_ref(tex);
-
- return BPyGPUTexture_CreatePyObject(tex);
+ return BPyGPUTexture_CreatePyObject(tex, true);
}
static struct PyMethodDef pygpu_texture__m_methods[] = {
@@ -595,13 +600,33 @@ PyObject *bpygpu_texture_init(void)
/** \name Public API
* \{ */
-PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex)
+PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex, bool shared_reference)
{
BPyGPUTexture *self;
+ if (shared_reference) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ void **ref = GPU_texture_py_reference_get(tex);
+ if (ref) {
+ /* Retrieve BPyGPUTexture reference. */
+ self = (BPyGPUTexture *)POINTER_OFFSET(ref, -offsetof(BPyGPUTexture, tex));
+ BLI_assert(self->tex == tex);
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+#endif
+
+ GPU_texture_ref(tex);
+ }
+
self = PyObject_New(BPyGPUTexture, &BPyGPUTexture_Type);
self->tex = tex;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ BLI_assert(GPU_texture_py_reference_get(tex) == NULL);
+ GPU_texture_py_reference_set(tex, (void **)&self->tex);
+#endif
+
return (PyObject *)self;
}
diff --git a/source/blender/python/gpu/gpu_py_texture.h b/source/blender/python/gpu/gpu_py_texture.h
index 5130273f971..3eaaa3411bd 100644
--- a/source/blender/python/gpu/gpu_py_texture.h
+++ b/source/blender/python/gpu/gpu_py_texture.h
@@ -33,4 +33,5 @@ typedef struct BPyGPUTexture {
int bpygpu_ParseTexture(PyObject *o, void *p);
PyObject *bpygpu_texture_init(void);
-PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex) ATTR_NONNULL(1);
+PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex, bool shared_reference)
+ ATTR_NONNULL(1);
diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.h b/source/blender/python/gpu/gpu_py_vertex_buffer.h
index 8adf2aa0792..d105faf9bc1 100644
--- a/source/blender/python/gpu/gpu_py_vertex_buffer.h
+++ b/source/blender/python/gpu/gpu_py_vertex_buffer.h
@@ -28,8 +28,8 @@ extern PyTypeObject BPyGPUVertBuf_Type;
typedef struct BPyGPUVertBuf {
PyObject_VAR_HEAD
- /* The buf is owned, we may support thin wrapped batches later. */
- struct GPUVertBuf *buf;
+ /* The buf is owned, we may support thin wrapped batches later. */
+ struct GPUVertBuf *buf;
} BPyGPUVertBuf;
PyObject *BPyGPUVertBuf_CreatePyObject(struct GPUVertBuf *buf) ATTR_NONNULL(1);
diff --git a/source/blender/python/gpu/gpu_py_vertex_format.h b/source/blender/python/gpu/gpu_py_vertex_format.h
index 54d090e2923..6c99d13cf03 100644
--- a/source/blender/python/gpu/gpu_py_vertex_format.h
+++ b/source/blender/python/gpu/gpu_py_vertex_format.h
@@ -27,7 +27,8 @@ extern PyTypeObject BPyGPUVertFormat_Type;
#define BPyGPUVertFormat_Check(v) (Py_TYPE(v) == &BPyGPUVertFormat_Type)
typedef struct BPyGPUVertFormat {
- PyObject_VAR_HEAD struct GPUVertFormat fmt;
+ PyObject_VAR_HEAD
+ struct GPUVertFormat fmt;
} BPyGPUVertFormat;
PyObject *BPyGPUVertFormat_CreatePyObject(struct GPUVertFormat *fmt);
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 9ac8d4d9f47..2be2105d327 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
bpy_rna_driver.c
bpy_rna_gizmo.c
bpy_rna_id_collection.c
+ bpy_rna_operator.c
bpy_rna_types_capi.c
bpy_rna_ui.c
bpy_traceback.c
@@ -118,6 +119,7 @@ set(SRC
bpy_rna_driver.h
bpy_rna_gizmo.h
bpy_rna_id_collection.h
+ bpy_rna_operator.h
bpy_rna_types_capi.h
bpy_rna_ui.h
bpy_traceback.h
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index 547cf2ad38f..dc9b8b52821 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -118,7 +118,7 @@ static PyObject *bpy_blend_paths(PyObject *UNUSED(self), PyObject *args, PyObjec
bool local = false;
static const char *_keywords[] = {"absolute", "packed", "local", NULL};
- static _PyArg_Parser _parser = {"|O&O&O&:blend_paths", _keywords, 0};
+ static _PyArg_Parser _parser = {"|$O&O&O&:blend_paths", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -164,8 +164,8 @@ static PyObject *bpy_user_resource(PyObject *UNUSED(self), PyObject *args, PyObj
const char *path;
- static const char *_keywords[] = {"type", "subdir", NULL};
- static _PyArg_Parser _parser = {"O&|s:user_resource", _keywords, 0};
+ static const char *_keywords[] = {"type", "path", NULL};
+ static _PyArg_Parser _parser = {"O&|$s:user_resource", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, PyC_ParseStringEnum, &type, &subdir)) {
return NULL;
}
@@ -201,7 +201,7 @@ static PyObject *bpy_system_resource(PyObject *UNUSED(self), PyObject *args, PyO
const char *path;
static const char *_keywords[] = {"type", "path", NULL};
- static _PyArg_Parser _parser = {"O&|s:system_resource", _keywords, 0};
+ static _PyArg_Parser _parser = {"O&|$s:system_resource", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, PyC_ParseStringEnum, &type, &subdir)) {
return NULL;
}
@@ -239,7 +239,7 @@ static PyObject *bpy_resource_path(PyObject *UNUSED(self), PyObject *args, PyObj
const char *path;
static const char *_keywords[] = {"type", "major", "minor", NULL};
- static _PyArg_Parser _parser = {"O&|ii:resource_path", _keywords, 0};
+ static _PyArg_Parser _parser = {"O&|$ii:resource_path", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kw, &_parser, PyC_ParseStringEnum, &type, &major, &minor)) {
return NULL;
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 927ec11c376..4de6063098b 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -82,7 +82,10 @@ static PyTypeObject BlenderAppType;
static PyStructSequence_Field app_info_fields[] = {
{"version", "The Blender version as a tuple of 3 numbers. eg. (2, 83, 1)"},
- {"version_file", "The blend file version, compatible with ``bpy.data.version``"},
+ {"version_file",
+ "The Blender version, as a tuple, last used to save a .blend file, compatible with "
+ "``bpy.data.version``. This value should be used for handling compatibility changes between "
+ "Blender versions"},
{"version_string", "The Blender version formatted as a string"},
{"version_cycle", "The release status of this build alpha/beta/rc/release"},
{"version_char", "Deprecated, always an empty string"},
diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c
index 8ecee9b3f2e..bc05c51414f 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -43,6 +43,9 @@ void bpy_app_generic_callback(struct Main *main,
static PyTypeObject BlenderAppCbType;
+/**
+ * See `BKE_callbacks.h` #eCbEvent declaration for the policy on naming.
+ */
static PyStructSequence_Field app_cb_info_fields[] = {
{"frame_change_pre",
"Called after frame change for playback and rendering, before any data is evaluated for the "
@@ -74,6 +77,7 @@ static PyStructSequence_Field app_cb_info_fields[] = {
{"version_update", "on ending the versioning code"},
{"load_factory_preferences_post", "on loading factory preferences (after)"},
{"load_factory_startup_post", "on loading factory startup (after)"},
+ {"xr_session_start_pre", "on starting an xr session (before)"},
/* sets the permanent tag */
#define APP_CB_OTHER_FIELDS 1
diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h
index 0854713982d..d7ee1eedabb 100644
--- a/source/blender/python/intern/bpy_capi_utils.h
+++ b/source/blender/python/intern/bpy_capi_utils.h
@@ -33,11 +33,6 @@ struct ReportList;
char *BPy_enum_as_string(const struct EnumPropertyItem *item);
-#define BLANK_PYTHON_TYPE \
- { \
- PyVarObject_HEAD_INIT(NULL, 0) NULL \
- }
-
/* error reporting */
short BPy_reports_to_error(struct ReportList *reports, PyObject *exception, const bool clear);
void BPy_reports_write_stdout(const struct ReportList *reports, const char *header);
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 5f31e0bb74d..f95e655d0c6 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -167,6 +167,14 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate)
}
}
+static void bpy_context_end(bContext *C)
+{
+ if (UNLIKELY(C == NULL)) {
+ return;
+ }
+ CTX_wm_operator_poll_msg_clear(C);
+}
+
/**
* Use for `CTX_*_set(..)` functions need to set values which are later read back as expected.
* In this case we don't want the Python context to override the values as it causes problems
@@ -392,7 +400,7 @@ void BPY_python_start(bContext *C, int argc, const char **argv)
/* Needed for Python's initialization for portable Python installations.
* We could use #Py_SetPath, but this overrides Python's internal logic
- * for calculating it's own module search paths.
+ * for calculating its own module search paths.
*
* `sys.executable` is overwritten after initialization to the Python binary. */
{
@@ -524,6 +532,9 @@ void BPY_python_end(void)
/* finalizing, no need to grab the state, except when we are a module */
gilstate = PyGILState_Ensure();
+ /* Clear Python values in the context so freeing the context after Python exits doesn't crash. */
+ bpy_context_end(BPY_context_get());
+
/* Decrement user counts of all callback functions. */
BPY_rna_props_clear_all();
diff --git a/source/blender/python/intern/bpy_interface_run.c b/source/blender/python/intern/bpy_interface_run.c
index a7593ae7d79..f7272a87a17 100644
--- a/source/blender/python/intern/bpy_interface_run.c
+++ b/source/blender/python/intern/bpy_interface_run.c
@@ -31,6 +31,7 @@
#include "BKE_context.h"
#include "BKE_main.h"
+#include "BKE_report.h"
#include "BKE_text.h"
#include "DNA_text_types.h"
@@ -294,13 +295,47 @@ bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr)
* in code that doesn't deal with Python data-types.
* \{ */
+static void run_string_handle_error(struct BPy_RunErrInfo *err_info)
+{
+ if (err_info == NULL) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ /* Signal to do nothing. */
+ if (!(err_info->reports || err_info->r_string)) {
+ PyErr_Clear();
+ return;
+ }
+
+ PyObject *py_err_str = err_info->use_single_line_error ? PyC_ExceptionBuffer_Simple() :
+ PyC_ExceptionBuffer();
+ const char *err_str = py_err_str ? PyUnicode_AsUTF8(py_err_str) : "Unable to extract exception";
+
+ if (err_info->reports != NULL) {
+ if (err_info->report_prefix) {
+ BKE_reportf(err_info->reports, RPT_ERROR, "%s: %s", err_info->report_prefix, err_str);
+ }
+ else {
+ BKE_report(err_info->reports, RPT_ERROR, err_str);
+ }
+ }
+
+ if (err_info->r_string != NULL) {
+ *err_info->r_string = BLI_strdup(err_str);
+ }
+
+ Py_XDECREF(py_err_str);
+}
+
/**
* \return success
*/
bool BPY_run_string_as_number(bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
double *r_value)
{
PyGILState_STATE gilstate;
@@ -320,12 +355,7 @@ bool BPY_run_string_as_number(bContext *C,
ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value);
if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
- }
- else {
- PyErr_Clear();
- }
+ run_string_handle_error(err_info);
}
bpy_context_clear(C, &gilstate);
@@ -339,7 +369,7 @@ bool BPY_run_string_as_number(bContext *C,
bool BPY_run_string_as_string_and_size(bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
char **r_value,
size_t *r_value_size)
{
@@ -357,12 +387,7 @@ bool BPY_run_string_as_string_and_size(bContext *C,
ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size);
if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix);
- }
- else {
- PyErr_Clear();
- }
+ run_string_handle_error(err_info);
}
bpy_context_clear(C, &gilstate);
@@ -373,12 +398,11 @@ bool BPY_run_string_as_string_and_size(bContext *C,
bool BPY_run_string_as_string(bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
char **r_value)
{
size_t value_dummy_size;
- return BPY_run_string_as_string_and_size(
- C, imports, expr, report_prefix, r_value, &value_dummy_size);
+ return BPY_run_string_as_string_and_size(C, imports, expr, err_info, r_value, &value_dummy_size);
}
/**
@@ -389,7 +413,7 @@ bool BPY_run_string_as_string(bContext *C,
bool BPY_run_string_as_intptr(bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
intptr_t *r_value)
{
BLI_assert(r_value && expr);
@@ -406,12 +430,7 @@ bool BPY_run_string_as_intptr(bContext *C,
ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value);
if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
- }
- else {
- PyErr_Clear();
- }
+ run_string_handle_error(err_info);
}
bpy_context_clear(C, &gilstate);
diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c
index 96ff6a111d9..7a688b8c77d 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -195,7 +195,7 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k
bool is_rel = false, is_link = false, use_assets_only = false;
static const char *_keywords[] = {"filepath", "link", "relative", "assets_only", NULL};
- static _PyArg_Parser _parser = {"s|O&O&O&:load", _keywords, 0};
+ static _PyArg_Parser _parser = {"s|$O&O&O&:load", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c
index 7cc7f864c9c..4e6d2aacb49 100644
--- a/source/blender/python/intern/bpy_msgbus.c
+++ b/source/blender/python/intern/bpy_msgbus.c
@@ -245,7 +245,7 @@ static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args
"options",
NULL,
};
- static _PyArg_Parser _parser = {"OOO!O|O!:subscribe_rna", _keywords, 0};
+ static _PyArg_Parser _parser = {"OOO!O|$O!:subscribe_rna", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index 94ad6a8ef78..4a5e2552598 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -244,12 +244,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
}
if (WM_operator_poll_context((bContext *)C, ot, context) == false) {
- const char *msg = CTX_wm_operator_poll_msg_get(C);
+ bool msg_free = false;
+ const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free);
PyErr_Format(PyExc_RuntimeError,
"Operator bpy.ops.%.200s.poll() %.200s",
opname,
msg ? msg : "failed, context is incorrect");
- CTX_wm_operator_poll_msg_set(C, NULL); /* better set to NULL else it could be used again */
+ CTX_wm_operator_poll_msg_clear(C);
+ if (msg_free) {
+ MEM_freeN((void *)msg);
+ }
error_val = -1;
}
else {
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index e4e6b3ea8f2..6ad80f9160b 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -147,7 +147,17 @@ static const EnumPropertyItem property_subtype_number_items[] = {
{PROP_PERCENTAGE, "PERCENTAGE", 0, "Percentage", ""},
{PROP_FACTOR, "FACTOR", 0, "Factor", ""},
{PROP_ANGLE, "ANGLE", 0, "Angle", ""},
- {PROP_TIME, "TIME", 0, "Time", ""},
+ {PROP_TIME,
+ "TIME",
+ 0,
+ "Time (Scene Relative)",
+ "Time specified in frames, converted to seconds based on scene frame rate"},
+ {PROP_TIME_ABSOLUTE,
+ "TIME_ABSOLUTE",
+ 0,
+ "Time (Absolute)",
+ "Time specified in seconds, independent of the scene"},
+ {PROP_TIME_ABSOLUTE, "TIME_ABSOLUTE", 0, "Time Absolute", ""},
{PROP_DISTANCE, "DISTANCE", 0, "Distance", ""},
{PROP_DISTANCE_CAMERA, "DISTANCE_CAMERA", 0, "Camera Distance", ""},
{PROP_POWER, "POWER", 0, "Power", ""},
@@ -2464,7 +2474,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssO&O!O!O!sOOO:BoolProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssO&O!O!O!sOOO:BoolProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2583,7 +2593,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOO!O!O!siOOO:BoolVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssOO!O!O!siOOO:BoolVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2728,7 +2738,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2865,7 +2875,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOiiiiiO!O!O!siOOO:IntVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssOiiiiiO!O!O!siOOO:IntVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3005,7 +3015,7 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
"soft_max", "step", "precision", "options", "override", "tags", "subtype",
"unit", "update", "get", "set", NULL,
};
- static _PyArg_Parser _parser = {"s#|ssffffffiO!O!O!ssOOO:FloatProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssffffffiO!O!O!ssOOO:FloatProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3138,7 +3148,7 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
"soft_max", "step", "precision", "options", "override", "tags", "subtype",
"unit", "size", "update", "get", "set", NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOfffffiO!O!O!ssiOOO:FloatVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssOfffffiO!O!O!ssiOOO:FloatVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3287,7 +3297,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|sssiO!O!O!sOOO:StringProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$sssiO!O!O!sOOO:StringProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3445,7 +3455,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssOO!O!O!OOO:EnumProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|$ssOO!O!O!OOO:EnumProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3628,7 +3638,7 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
"update",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssO!O!O!OO:PointerProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|$ssO!O!O!OO:PointerProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3731,7 +3741,7 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
"tags",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssO!O!O!:CollectionProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|$ssO!O!O!:CollectionProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 49ac2662a31..fb1cb823964 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -2546,7 +2546,7 @@ static int pyrna_prop_collection_subscript_str_lib_pair_ptr(BPy_PropertyRNA *sel
if (lib == NULL) {
if (err_not_found) {
PyErr_Format(PyExc_KeyError,
- "%s: lib name '%.240s' "
+ "%s: lib filepath '%.1024s' "
"does not reference a valid library",
err_prefix,
keylib_str);
@@ -3562,7 +3562,7 @@ PyDoc_STRVAR(pyrna_struct_keys_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property keys.\n"
- " :rtype: list of strings\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewKeys`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
{
@@ -3573,13 +3573,9 @@ static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetKeys(group);
+ return BPy_Wrap_GetKeys_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_items_doc,
@@ -3589,7 +3585,7 @@ PyDoc_STRVAR(pyrna_struct_items_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property key, value pairs.\n"
- " :rtype: list of key, value tuples\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewItems`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
{
@@ -3600,13 +3596,9 @@ static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetItems(self->ptr.owner_id, group);
+ return BPy_Wrap_GetItems_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_values_doc,
@@ -3616,7 +3608,7 @@ PyDoc_STRVAR(pyrna_struct_values_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property values.\n"
- " :rtype: list\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewValues`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
{
@@ -3628,13 +3620,9 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetValues(self->ptr.owner_id, group);
+ return BPy_Wrap_GetValues_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_is_property_set_doc,
@@ -4207,6 +4195,10 @@ static void pyrna_dir_members_rna(PyObject *list, PointerRNA *ptr)
iterprop = RNA_struct_iterator_property(ptr->type);
RNA_PROP_BEGIN (ptr, itemptr, iterprop) {
+ /* Custom-properties are exposed using `__getitem__`, exclude from `__dir__`. */
+ if (RNA_property_is_idprop(itemptr.data)) {
+ continue;
+ }
nameptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &namelen);
if (nameptr) {
@@ -5002,8 +4994,13 @@ static PyObject *pyrna_struct_pop(BPy_StructRNA *self, PyObject *args)
idprop = IDP_GetPropertyFromGroup(group, key);
if (idprop) {
- PyObject *ret = BPy_IDGroup_WrapData(self->ptr.owner_id, idprop, group);
- IDP_RemoveFromGroup(group, idprop);
+ /* Don't use #BPy_IDGroup_WrapData as the id-property is being removed from the ID. */
+ PyObject *ret = BPy_IDGroup_MapDataToPy(idprop);
+ /* Internal error. */
+ if (UNLIKELY(ret == NULL)) {
+ return NULL;
+ }
+ IDP_FreeFromGroup(group, idprop);
return ret;
}
}
diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c
index b7648ee830f..b359e93315e 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -324,7 +324,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
if (pyrna_struct_keyframe_parse(&self->ptr,
args,
kw,
- "s|ifsO!:bpy_struct.keyframe_insert()",
+ "s|$ifsO!:bpy_struct.keyframe_insert()",
"bpy_struct.keyframe_insert()",
&path_full,
&index,
@@ -443,7 +443,7 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb
if (pyrna_struct_keyframe_parse(&self->ptr,
args,
kw,
- "s|ifsO!:bpy_struct.keyframe_delete()",
+ "s|$ifsO!:bpy_struct.keyframe_delete()",
"bpy_struct.keyframe_insert()",
&path_full,
&index,
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index d4127b26629..ac061c3dd60 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -171,7 +171,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *
IDUserMapData data_cb = {NULL};
static const char *_keywords[] = {"subset", "key_types", "value_types", NULL};
- static _PyArg_Parser _parser = {"|O$O!O!:user_map", _keywords, 0};
+ static _PyArg_Parser _parser = {"|$OO!O!:user_map", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kwds, &_parser, &subset, &PySet_Type, &key_types, &PySet_Type, &val_types)) {
return NULL;
diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c
new file mode 100644
index 00000000000..490d9aa5212
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_operator.c
@@ -0,0 +1,152 @@
+/*
+ * 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 pythonintern
+ *
+ * This file extends `bpy.types.Operator` with C/Python API methods and attributes.
+ */
+
+#include <Python.h>
+
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+
+#include "../generic/python_utildefines.h"
+
+#include "BPY_extern.h"
+#include "bpy_capi_utils.h"
+
+#include "bpy_rna_operator.h" /* Own include. */
+
+/* -------------------------------------------------------------------- */
+/** \name Operator `poll_message_set` Method
+ * \{ */
+
+static char *pyop_poll_message_get_fn(bContext *UNUSED(C), void *user_data)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ PyObject *py_args = user_data;
+ PyObject *py_func_or_msg = PyTuple_GET_ITEM(py_args, 0);
+
+ if (PyUnicode_Check(py_func_or_msg)) {
+ return BLI_strdup(PyUnicode_AsUTF8(py_func_or_msg));
+ }
+
+ PyObject *py_args_after_first = PyTuple_GetSlice(py_args, 1, PY_SSIZE_T_MAX);
+ PyObject *py_msg = PyObject_CallObject(py_func_or_msg, py_args_after_first);
+ Py_DECREF(py_args_after_first);
+
+ char *msg = NULL;
+ bool error = false;
+
+ /* NULL for no string. */
+ if (py_msg == NULL) {
+ error = true;
+ }
+ else {
+ if (py_msg == Py_None) {
+ /* pass */
+ }
+ else if (PyUnicode_Check(py_msg)) {
+ msg = BLI_strdup(PyUnicode_AsUTF8(py_msg));
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "poll_message_set(function, ...): expected string or None, got %.200s",
+ Py_TYPE(py_msg)->tp_name);
+ error = true;
+ }
+ Py_DECREF(py_msg);
+ }
+
+ if (error) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ PyGILState_Release(gilstate);
+ return msg;
+}
+
+static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data)
+{
+ /* Handles the GIL. */
+ BPY_DECREF(user_data);
+}
+
+PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc,
+ ".. method:: poll_message_set(message, ...)\n"
+ "\n"
+ " Set the message to show in the tool-tip when poll fails.\n"
+ "\n"
+ " When message is callable, "
+ "additional user defined positional arguments are passed to the message function.\n"
+ "\n"
+ " :param message: The message or a function that returns the message.\n"
+ " :type message: string or a callable that returns a string or None.\n");
+
+static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args)
+{
+ const ssize_t args_len = PyTuple_GET_SIZE(args);
+ if (args_len == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "poll_message_set(message, ...): requires a message argument");
+ return NULL;
+ }
+
+ PyObject *py_func_or_msg = PyTuple_GET_ITEM(args, 0);
+
+ if (PyUnicode_Check(py_func_or_msg)) {
+ if (args_len > 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "poll_message_set(message): does not support additional arguments");
+ return NULL;
+ }
+ }
+ else if (PyCallable_Check(py_func_or_msg)) {
+ /* pass */
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "poll_message_set(message, ...): "
+ "expected at least 1 string or callable argument, got %.200s",
+ Py_TYPE(py_func_or_msg)->tp_name);
+ return NULL;
+ }
+
+ bContext *C = BPY_context_get();
+ struct bContextPollMsgDyn_Params params = {
+ .get_fn = pyop_poll_message_get_fn,
+ .free_fn = pyop_poll_message_free_fn,
+ .user_data = Py_INCREF_RET(args),
+ };
+
+ CTX_wm_operator_poll_msg_set_dynamic(C, &params);
+
+ Py_RETURN_NONE;
+}
+
+PyMethodDef BPY_rna_operator_poll_message_set_method_def = {
+ "poll_message_set",
+ (PyCFunction)BPY_rna_operator_poll_message_set,
+ METH_VARARGS | METH_STATIC,
+ BPY_rna_operator_poll_message_set_doc,
+};
+
+/** \} */
diff --git a/source/blender/python/intern/bpy_rna_operator.h b/source/blender/python/intern/bpy_rna_operator.h
new file mode 100644
index 00000000000..8040d8a492a
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_operator.h
@@ -0,0 +1,31 @@
+/*
+ * 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 pythonintern
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern PyMethodDef BPY_rna_operator_poll_message_set_method_def;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c
index 9b15e84663d..2f6e197d1e2 100644
--- a/source/blender/python/intern/bpy_rna_types_capi.c
+++ b/source/blender/python/intern/bpy_rna_types_capi.c
@@ -41,6 +41,8 @@
#include "bpy_rna_types_capi.h"
#include "bpy_rna_ui.h"
+#include "bpy_rna_operator.h"
+
#include "../generic/py_capi_utils.h"
#include "RNA_access.h"
@@ -87,6 +89,17 @@ static struct PyMethodDef pyrna_uilayout_methods[] = {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Operator
+ * \{ */
+
+static struct PyMethodDef pyrna_operator_methods[] = {
+ {NULL, NULL, 0, NULL}, /* #BPY_rna_operator_poll_message_set */
+ {NULL, NULL, 0, NULL},
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Window Manager Clipboard Property
*
* Avoid using the RNA API because this value may change between checking its length
@@ -228,6 +241,11 @@ void BPY_rna_types_extend_capi(void)
/* Space */
pyrna_struct_type_extend_capi(&RNA_Space, pyrna_space_methods, NULL);
+ /* wmOperator */
+ ARRAY_SET_ITEMS(pyrna_operator_methods, BPY_rna_operator_poll_message_set_method_def);
+ BLI_assert(ARRAY_SIZE(pyrna_operator_methods) == 2);
+ pyrna_struct_type_extend_capi(&RNA_Operator, pyrna_operator_methods, NULL);
+
/* WindowManager */
pyrna_struct_type_extend_capi(
&RNA_WindowManager, pyrna_windowmanager_methods, pyrna_windowmanager_getset);
diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c
index b460ef5e0c0..e5ac1ba9a95 100644
--- a/source/blender/python/intern/bpy_utils_units.c
+++ b/source/blender/python/intern/bpy_utils_units.c
@@ -183,7 +183,7 @@ static PyObject *bpyunits_to_value(PyObject *UNUSED(self), PyObject *args, PyObj
"str_ref_unit",
NULL,
};
- static _PyArg_Parser _parser = {"sss#|z:to_value", _keywords, 0};
+ static _PyArg_Parser _parser = {"sss#|$z:to_value", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kw, &_parser, &usys_str, &ucat_str, &inpt, &str_len, &uref)) {
return NULL;
@@ -260,7 +260,7 @@ static PyObject *bpyunits_to_string(PyObject *UNUSED(self), PyObject *args, PyOb
"compatible_unit",
NULL,
};
- static _PyArg_Parser _parser = {"ssd|iO&O&:to_string", _keywords, 0};
+ static _PyArg_Parser _parser = {"ssd|$iO&O&:to_string", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 3791a6c2d29..16bf7120606 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -152,7 +152,7 @@ int mathutils_array_parse(
return -1;
}
- memcpy(array, ((BaseMathObject *)value)->data, size * sizeof(float));
+ memcpy(array, ((const BaseMathObject *)value)->data, size * sizeof(float));
}
else
#endif
@@ -235,7 +235,7 @@ int mathutils_array_parse_alloc(float **array,
}
*array = PyMem_Malloc(size * sizeof(float));
- memcpy(*array, ((BaseMathObject *)value)->data, size * sizeof(float));
+ memcpy(*array, ((const BaseMathObject *)value)->data, size * sizeof(float));
return size;
}
@@ -471,7 +471,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error
return -1;
}
- eulO_to_mat3(rmat, ((EulerObject *)value)->eul, ((EulerObject *)value)->order);
+ eulO_to_mat3(rmat, ((const EulerObject *)value)->eul, ((const EulerObject *)value)->order);
return 0;
}
if (QuaternionObject_Check(value)) {
@@ -480,7 +480,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error
}
float tquat[4];
- normalize_qt_qt(tquat, ((QuaternionObject *)value)->quat);
+ normalize_qt_qt(tquat, ((const QuaternionObject *)value)->quat);
quat_to_mat3(rmat, tquat);
return 0;
}
@@ -530,8 +530,8 @@ int EXPP_FloatsAreEqual(float af, float bf, int maxDiff)
{
/* solid, fast routine across all platforms
* with constant time behavior */
- const int ai = *(int *)(&af);
- const int bi = *(int *)(&bf);
+ const int ai = *(const int *)(&af);
+ const int bi = *(const int *)(&bf);
const int test = SIGNMASK(ai ^ bi);
int diff, v1, v2;
diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h
index 75aa08ee1df..80be841785a 100644
--- a/source/blender/python/mathutils/mathutils.h
+++ b/source/blender/python/mathutils/mathutils.h
@@ -52,7 +52,8 @@ enum {
#define BASE_MATH_MEMBERS(_data) \
/** Array of data (alias), wrapped status depends on wrapped status. */ \
- PyObject_VAR_HEAD float *_data; \
+ PyObject_VAR_HEAD \
+ float *_data; \
/** If this vector references another object, otherwise NULL, *Note* this owns its reference */ \
PyObject *cb_user; \
/** Which user funcs do we adhere to, RNA, etc */ \
diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c
index f2a8af18073..b6a0183d04e 100644
--- a/source/blender/python/mathutils/mathutils_Euler.c
+++ b/source/blender/python/mathutils/mathutils_Euler.c
@@ -88,7 +88,7 @@ short euler_order_from_string(const char *str, const char *error_prefix)
# define MAKE_ID3(a, b, c) (((a) << 24) | ((b) << 16) | ((c) << 8))
#endif
- switch (*((PY_INT32_T *)str)) {
+ switch (*((const PY_INT32_T *)str)) {
case MAKE_ID3('X', 'Y', 'Z'):
return EULER_ORDER_XYZ;
case MAKE_ID3('X', 'Z', 'Y'):
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 161d2f41592..5d38a3692c3 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -187,7 +187,7 @@ static int mathutils_matrix_col_get(BaseMathObject *bmo, int col)
}
/* for 'translation' size will always be '3' even on 4x4 vec */
- num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size);
+ num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size);
for (row = 0; row < num_row; row++) {
bmo->data[row] = MATRIX_ITEM(self, row, col);
@@ -210,7 +210,7 @@ static int mathutils_matrix_col_set(BaseMathObject *bmo, int col)
}
/* for 'translation' size will always be '3' even on 4x4 vec */
- num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size);
+ num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size);
for (row = 0; row < num_row; row++) {
MATRIX_ITEM(self, row, col) = bmo->data[row];
@@ -969,6 +969,104 @@ static PyObject *C_Matrix_Shear(PyObject *cls, PyObject *args)
return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls);
}
+PyDoc_STRVAR(
+ C_Matrix_LocRotScale_doc,
+ ".. classmethod:: LocRotScale(location, rotation, scale)\n"
+ "\n"
+ " Create a matrix combining translation, rotation and scale,\n"
+ " acting as the inverse of the decompose() method.\n"
+ "\n"
+ " Any of the inputs may be replaced with None if not needed.\n"
+ "\n"
+ " :arg location: The translation component.\n"
+ " :type location: :class:`Vector` or None\n"
+ " :arg rotation: The rotation component.\n"
+ " :type rotation: 3x3 :class:`Matrix`, :class:`Quaternion`, :class:`Euler` or None\n"
+ " :arg scale: The scale component.\n"
+ " :type scale: :class:`Vector` or None\n"
+ " :return: Combined transformation matrix. \n"
+ " :rtype: 4x4 :class:`Matrix`\n");
+static PyObject *C_Matrix_LocRotScale(PyObject *cls, PyObject *args)
+{
+ PyObject *loc_obj, *rot_obj, *scale_obj;
+ float mat[4][4], loc[3];
+
+ if (!PyArg_ParseTuple(args, "OOO:Matrix.LocRotScale", &loc_obj, &rot_obj, &scale_obj)) {
+ return NULL;
+ }
+
+ /* Decode location. */
+ if (loc_obj == Py_None) {
+ zero_v3(loc);
+ }
+ else if (mathutils_array_parse(
+ loc, 3, 3, loc_obj, "Matrix.LocRotScale(), invalid location argument") == -1) {
+ return NULL;
+ }
+
+ /* Decode rotation. */
+ if (rot_obj == Py_None) {
+ unit_m4(mat);
+ }
+ else if (QuaternionObject_Check(rot_obj)) {
+ QuaternionObject *quat_obj = (QuaternionObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(quat_obj) == -1) {
+ return NULL;
+ }
+
+ quat_to_mat4(mat, quat_obj->quat);
+ }
+ else if (EulerObject_Check(rot_obj)) {
+ EulerObject *eul_obj = (EulerObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(eul_obj) == -1) {
+ return NULL;
+ }
+
+ eulO_to_mat4(mat, eul_obj->eul, eul_obj->order);
+ }
+ else if (MatrixObject_Check(rot_obj)) {
+ MatrixObject *mat_obj = (MatrixObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(mat_obj) == -1) {
+ return NULL;
+ }
+
+ if (mat_obj->num_col == 3 && mat_obj->num_row == 3) {
+ copy_m4_m3(mat, (const float(*)[3])mat_obj->matrix);
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "Matrix.LocRotScale(): "
+ "inappropriate rotation matrix size - expects 3x3 matrix");
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "Matrix.LocRotScale(): "
+ "rotation argument must be Matrix, Quaternion, Euler or None");
+ return NULL;
+ }
+
+ /* Decode scale. */
+ if (scale_obj != Py_None) {
+ float scale[3];
+
+ if (mathutils_array_parse(
+ scale, 3, 3, scale_obj, "Matrix.LocRotScale(), invalid scale argument") == -1) {
+ return NULL;
+ }
+
+ rescale_m4(mat, scale);
+ }
+
+ copy_v3_v3(mat[3], loc);
+
+ return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls);
+}
+
void matrix_as_3x3(float mat[3][3], MatrixObject *self)
{
copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0));
@@ -1029,7 +1127,7 @@ static float matrix_determinant_internal(const MatrixObject *self)
MATRIX_ITEM(self, 2, 2));
}
- return determinant_m4((float(*)[4])self->matrix);
+ return determinant_m4((const float(*)[4])self->matrix);
}
static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim)
@@ -1037,15 +1135,15 @@ static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort
/* calculate the classical adjoint */
switch (dim) {
case 2: {
- adjoint_m2_m2((float(*)[2])mat_dst, (float(*)[2])mat_src);
+ adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src);
break;
}
case 3: {
- adjoint_m3_m3((float(*)[3])mat_dst, (float(*)[3])mat_src);
+ adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src);
break;
}
case 4: {
- adjoint_m4_m4((float(*)[4])mat_dst, (float(*)[4])mat_src);
+ adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src);
break;
}
default:
@@ -1115,7 +1213,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[2] = (float(*)[2])in_mat;
if (in_mat != self->matrix) {
- copy_m2_m2(mat, (float(*)[2])self->matrix);
+ copy_m2_m2(mat, (const float(*)[2])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1130,7 +1228,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[3] = (float(*)[3])in_mat;
if (in_mat != self->matrix) {
- copy_m3_m3(mat, (float(*)[3])self->matrix);
+ copy_m3_m3(mat, (const float(*)[3])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1146,7 +1244,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[4] = (float(*)[4])in_mat;
if (in_mat != self->matrix) {
- copy_m4_m4(mat, (float(*)[4])self->matrix);
+ copy_m4_m4(mat, (const float(*)[4])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1194,7 +1292,7 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self)
mat3_to_quat(quat, (float(*)[3])self->matrix);
}
else {
- mat4_to_quat(quat, (float(*)[4])self->matrix);
+ mat4_to_quat(quat, (const float(*)[4])self->matrix);
}
return Quaternion_CreatePyObject(quat, NULL);
@@ -1243,10 +1341,10 @@ static PyObject *Matrix_to_euler(MatrixObject *self, PyObject *args)
/*must be 3-4 cols, 3-4 rows, square matrix */
if (self->num_row == 3 && self->num_col == 3) {
- copy_m3_m3(mat, (float(*)[3])self->matrix);
+ copy_m3_m3(mat, (const float(*)[3])self->matrix);
}
else if (self->num_row == 4 && self->num_col == 4) {
- copy_m3_m4(mat, (float(*)[4])self->matrix);
+ copy_m3_m4(mat, (const float(*)[4])self->matrix);
}
else {
PyErr_SetString(PyExc_ValueError,
@@ -1321,7 +1419,7 @@ static PyObject *Matrix_resize_4x4(MatrixObject *self)
memcpy(mat[col], MATRIX_COL_PTR(self, col), self->num_row * sizeof(float));
}
- copy_m4_m4((float(*)[4])self->matrix, (float(*)[4])mat);
+ copy_m4_m4((float(*)[4])self->matrix, (const float(*)[4])mat);
self->num_col = 4;
self->num_row = 4;
@@ -1479,7 +1577,7 @@ static bool matrix_invert_args_check(const MatrixObject *self, PyObject *args, b
return true;
case 1:
if (check_type) {
- const MatrixObject *fallback = (MatrixObject *)PyTuple_GET_ITEM(args, 0);
+ const MatrixObject *fallback = (const MatrixObject *)PyTuple_GET_ITEM(args, 0);
if (!MatrixObject_Check(fallback)) {
PyErr_SetString(PyExc_TypeError,
"Matrix.invert: "
@@ -1797,7 +1895,7 @@ static PyObject *Matrix_decompose(MatrixObject *self)
return NULL;
}
- mat4_to_loc_rot_size(loc, rot, size, (float(*)[4])self->matrix);
+ mat4_to_loc_rot_size(loc, rot, size, (const float(*)[4])self->matrix);
mat3_to_quat(quat, rot);
ret = PyTuple_New(3);
@@ -2059,7 +2157,7 @@ static PyObject *Matrix_identity(MatrixObject *self)
static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix)
{
- return Matrix_CreatePyObject((float *)matrix, self->num_col, self->num_row, Py_TYPE(self));
+ return Matrix_CreatePyObject((const float *)matrix, self->num_col, self->num_row, Py_TYPE(self));
}
PyDoc_STRVAR(Matrix_copy_doc,
@@ -2155,7 +2253,7 @@ static PyObject *Matrix_str(MatrixObject *self)
for (col = 0; col < self->num_col; col++) {
maxsize[col] = 0;
for (row = 0; row < self->num_row; row++) {
- const int size = BLI_snprintf(
+ const int size = BLI_snprintf_rlen(
dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col));
maxsize[col] = max_ii(maxsize[col], size);
}
@@ -2960,10 +3058,10 @@ static PyObject *Matrix_is_negative_get(MatrixObject *self, void *UNUSED(closure
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_negative_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_negative_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_negative_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_negative_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -2982,10 +3080,10 @@ static PyObject *Matrix_is_orthogonal_get(MatrixObject *self, void *UNUSED(closu
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_orthonormal_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_orthonormal_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_orthonormal_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_orthonormal_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -3005,10 +3103,10 @@ static PyObject *Matrix_is_orthogonal_axis_vectors_get(MatrixObject *self, void
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_orthogonal_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_orthogonal_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_orthogonal_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_orthogonal_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -3111,6 +3209,10 @@ static struct PyMethodDef Matrix_methods[] = {
(PyCFunction)C_Matrix_OrthoProjection,
METH_VARARGS | METH_CLASS,
C_Matrix_OrthoProjection_doc},
+ {"LocRotScale",
+ (PyCFunction)C_Matrix_LocRotScale,
+ METH_VARARGS | METH_CLASS,
+ C_Matrix_LocRotScale_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c
index 1acbcc006ca..b00dbacad15 100644
--- a/source/blender/python/mathutils/mathutils_bvhtree.c
+++ b/source/blender/python/mathutils/mathutils_bvhtree.c
@@ -434,7 +434,7 @@ static void py_bvhtree_nearest_point_range_cb(void *userdata,
struct PyBVH_RangeData *data = userdata;
PyBVHTree *self = data->self;
- const float(*coords)[3] = (const float(*)[3])self->coords;
+ const float(*coords)[3] = self->coords;
const uint *tri = self->tris[index];
const float *tri_co[3] = {coords[tri[0]], coords[tri[1]], coords[tri[2]]};
float nearest_tmp[3], dist_sq;
@@ -961,8 +961,6 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb
/* Get data for tessellation */
{
- int tris_len_dummy;
-
coords_len = (uint)bm->totvert;
tris_len = (uint)poly_to_tri_count(bm->totface, bm->totloop);
@@ -971,8 +969,7 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb
looptris = MEM_mallocN(sizeof(*looptris) * (size_t)tris_len, __func__);
- BM_mesh_calc_tessellation(bm, looptris, &tris_len_dummy);
- BLI_assert(tris_len_dummy == (int)tris_len);
+ BM_mesh_calc_tessellation(bm, looptris);
}
{
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index 4b09c08f62c..1304447be65 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -349,7 +349,7 @@ static PyObject *M_Geometry_normal(PyObject *UNUSED(self), PyObject *args)
goto finally;
}
- normal_poly_v3(n, (const float(*)[3])coords, coords_len);
+ normal_poly_v3(n, coords, coords_len);
ret = Vector_CreatePyObject(n, 3, NULL);
finally:
diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c
index 6abb66899d5..bb6a7720c44 100644
--- a/source/blender/python/mathutils/mathutils_interpolate.c
+++ b/source/blender/python/mathutils/mathutils_interpolate.c
@@ -54,24 +54,15 @@ static PyObject *M_Interpolate_poly_3d_calc(PyObject *UNUSED(self), PyObject *ar
PyObject *point, *veclist, *ret;
int i;
- if (!PyArg_ParseTuple(args, "OO!:poly_3d_calc", &veclist, &vector_Type, &point)) {
+ if (!PyArg_ParseTuple(args, "OO:poly_3d_calc", &veclist, &point)) {
return NULL;
}
- if (BaseMath_ReadCallback((VectorObject *)point) == -1) {
+ if (mathutils_array_parse(
+ fp, 2, 3 | MU_ARRAY_ZERO, point, "pt must be a 2-3 dimensional vector") == -1) {
return NULL;
}
- fp[0] = ((VectorObject *)point)->vec[0];
- fp[1] = ((VectorObject *)point)->vec[1];
- if (((VectorObject *)point)->size > 2) {
- fp[2] = ((VectorObject *)point)->vec[2];
- }
- else {
- /* if its a 2d vector then set the z to be zero */
- fp[2] = 0.0f;
- }
-
len = mathutils_array_parse_alloc_v(((float **)&vecs), 3, veclist, __func__);
if (len == -1) {
return NULL;
diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c
index fe8f9ec0334..1ff574fefa8 100644
--- a/source/blender/python/mathutils/mathutils_kdtree.c
+++ b/source/blender/python/mathutils/mathutils_kdtree.c
@@ -52,7 +52,7 @@ static void kdtree_nearest_to_py_tuple(const KDTreeNearest_3d *nearest, PyObject
BLI_assert(PyTuple_GET_SIZE(py_retval) == 3);
PyTuple_SET_ITEMS(py_retval,
- Vector_CreatePyObject((float *)nearest->co, 3, NULL),
+ Vector_CreatePyObject(nearest->co, 3, NULL),
PyLong_FromLong(nearest->index),
PyFloat_FromDouble(nearest->dist));
}
@@ -222,7 +222,7 @@ static PyObject *py_kdtree_find(PyKDTree *self, PyObject *args, PyObject *kwargs
const char *keywords[] = {"co", "filter", NULL};
if (!PyArg_ParseTupleAndKeywords(
- args, kwargs, "O|O:find", (char **)keywords, &py_co, &py_filter)) {
+ args, kwargs, "O|$O:find", (char **)keywords, &py_co, &py_filter)) {
return NULL;
}
diff --git a/source/blender/python/rna_dump.py b/source/blender/python/rna_dump.py
index 7d469d20110..bd45a36e8a4 100644
--- a/source/blender/python/rna_dump.py
+++ b/source/blender/python/rna_dump.py
@@ -62,7 +62,7 @@ def seek(r, txt, recurs):
# print(dir(r))
# basic types
- if type_r in (float, int, bool, type(None)):
+ if type_r in {float, int, bool, type(None)}:
if PRINT_DATA:
print(txt + ' -> ' + str(r))
return
diff --git a/source/blender/python/simple_enum_gen.py b/source/blender/python/simple_enum_gen.py
deleted file mode 100644
index 861701f4b4c..00000000000
--- a/source/blender/python/simple_enum_gen.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# ***** 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>
-
-defs = """
- SPACE_EMPTY,
- SPACE_VIEW3D,
- SPACE_IPO,
- SPACE_OUTLINER,
- SPACE_BUTS,
- SPACE_FILE,
- SPACE_IMAGE,
- SPACE_INFO,
- SPACE_SEQ,
- SPACE_TEXT,
- SPACE_IMASEL, #Deprecated
- SPACE_SOUND, #Deprecated
- SPACE_ACTION,
- SPACE_NLA,
- SPACE_SCRIPT, #Deprecated
- SPACE_TIME, #Deprecated
- SPACE_NODE,
- SPACEICONMAX
-"""
-
-print('\tmod = PyModule_New("dummy");')
-print('\tPyModule_AddObject(submodule, "key", mod);')
-
-for d in defs.split('\n'):
-
- d = d.replace(',', ' ')
- w = d.split()
-
- if not w:
- continue
-
- try:
- w.remove("#define")
- except:
- pass
-
- # print w
-
- val = w[0]
- py_val = w[0]
-
- print('\tPyModule_AddObject(mod, "%s", PyLong_FromSize_t(%s));' % (val, py_val))
diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h
index c31a41f66d5..7352ac7b12e 100644
--- a/source/blender/render/RE_engine.h
+++ b/source/blender/render/RE_engine.h
@@ -195,7 +195,7 @@ float RE_engine_get_camera_shift_x(RenderEngine *engine,
void RE_engine_get_camera_model_matrix(RenderEngine *engine,
struct Object *camera,
bool use_spherical_stereo,
- float *r_modelmat);
+ float r_modelmat[16]);
bool RE_engine_get_spherical_stereo(RenderEngine *engine, struct Object *camera);
bool RE_engine_test_break(RenderEngine *engine);
@@ -224,6 +224,8 @@ void RE_engine_register_pass(struct RenderEngine *engine,
const char *chanid,
eNodeSocketDatatype type);
+bool RE_engine_use_persistent_data(struct RenderEngine *engine);
+
/* Engine Types */
void RE_engines_init(void);
diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h
index 27dcd9e70ed..4534c86f7f7 100644
--- a/source/blender/render/RE_pipeline.h
+++ b/source/blender/render/RE_pipeline.h
@@ -184,14 +184,14 @@ void RE_InitRenderCB(struct Render *re);
void RE_FreeRender(struct Render *re);
/* only called on exit */
void RE_FreeAllRender(void);
-/* Free memory used by persistent data.
- * Invoked when loading new file.
- */
-void RE_FreeAllPersistentData(void);
-/* only call on file load */
+
+/* On file load, free render results. */
void RE_FreeAllRenderResults(void);
-/* for external render engines that can keep persistent data */
-void RE_FreePersistentData(void);
+/* On file load or changes engines, free persistent render data.
+ * Assumes no engines are currently rendering. */
+void RE_FreeAllPersistentData(void);
+/* Free persistent render data, optionally only for the given scene. */
+void RE_FreePersistentData(const Scene *scene);
/* get results and statistics */
void RE_FreeRenderResult(struct RenderResult *rr);
@@ -245,8 +245,6 @@ void RE_InitState(struct Render *re,
int winx,
int winy,
rcti *disprect);
-void RE_ChangeResolution(struct Render *re, int winx, int winy, rcti *disprect);
-void RE_ChangeModeFlag(struct Render *re, int flag, bool clear);
/* set up the viewplane/perspective matrix, three choices */
struct Object *RE_GetCamera(struct Render *re); /* return camera override if set */
@@ -297,9 +295,6 @@ void RE_RenderFreestyleStrokes(struct Render *re,
void RE_RenderFreestyleExternal(struct Render *re);
#endif
-/* Free memory and clear runtime data which is only needed during rendering. */
-void RE_CleanAfterRender(struct Render *re);
-
void RE_SetActiveRenderView(struct Render *re, const char *viewname);
const char *RE_GetActiveRenderView(struct Render *re);
diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c
index a43a78f5d3d..306d144f79d 100644
--- a/source/blender/render/intern/engine.c
+++ b/source/blender/render/intern/engine.c
@@ -140,6 +140,24 @@ RenderEngine *RE_engine_create(RenderEngineType *type)
return engine;
}
+static void engine_depsgraph_free(RenderEngine *engine)
+{
+ if (engine->depsgraph) {
+ /* Need GPU context since this might free GPU buffers. */
+ const bool use_gpu_context = (engine->type->flag & RE_USE_GPU_CONTEXT);
+ if (use_gpu_context) {
+ DRW_render_context_enable(engine->re);
+ }
+
+ DEG_graph_free(engine->depsgraph);
+ engine->depsgraph = NULL;
+
+ if (use_gpu_context) {
+ DRW_render_context_disable(engine->re);
+ }
+ }
+}
+
void RE_engine_free(RenderEngine *engine)
{
#ifdef WITH_PYTHON
@@ -148,6 +166,8 @@ void RE_engine_free(RenderEngine *engine)
}
#endif
+ engine_depsgraph_free(engine);
+
BLI_mutex_end(&engine->update_render_passes_mutex);
MEM_freeN(engine);
@@ -526,7 +546,7 @@ float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool us
void RE_engine_get_camera_model_matrix(RenderEngine *engine,
Object *camera,
bool use_spherical_stereo,
- float *r_modelmat)
+ float r_modelmat[16])
{
/* When using spherical stereo, get model matrix without multiview,
* leaving stereo to be handled by the engine. */
@@ -598,34 +618,94 @@ RenderData *RE_engine_get_render_data(Render *re)
return &re->r;
}
+bool RE_engine_use_persistent_data(RenderEngine *engine)
+{
+ /* Re-rendering is not supported with GPU contexts, since the GPU context
+ * is destroyed when the render thread exists. */
+ return (engine->re->r.mode & R_PERSISTENT_DATA) && !(engine->type->flag & RE_USE_GPU_CONTEXT);
+}
+
+static bool engine_keep_depsgraph(RenderEngine *engine)
+{
+ /* For persistent data or GPU engines like Eevee, reuse the depsgraph between
+ * view layers and animation frames. For renderers like Cycles that create
+ * their own copy of the scene, persistent data must be explicitly enabled to
+ * keep memory usage low by default. */
+ return (engine->re->r.mode & R_PERSISTENT_DATA) || (engine->type->flag & RE_USE_GPU_CONTEXT);
+}
+
/* Depsgraph */
static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer)
{
Main *bmain = engine->re->main;
Scene *scene = engine->re->scene;
+ bool reuse_depsgraph = false;
+
+ /* Reuse depsgraph from persistent data if possible. */
+ if (engine->depsgraph) {
+ if (DEG_get_bmain(engine->depsgraph) != bmain ||
+ DEG_get_input_scene(engine->depsgraph) != scene) {
+ /* If bmain or scene changes, we need a completely new graph. */
+ engine_depsgraph_free(engine);
+ }
+ else if (DEG_get_input_view_layer(engine->depsgraph) != view_layer) {
+ /* If only view layer changed, reuse depsgraph in the hope of reusing
+ * objects shared between view layers. */
+ DEG_graph_replace_owners(engine->depsgraph, bmain, scene, view_layer);
+ DEG_graph_tag_relations_update(engine->depsgraph);
+ }
+
+ reuse_depsgraph = true;
+ }
+
+ if (!engine->depsgraph) {
+ /* Ensure we only use persistent data for one scene / view layer at a time,
+ * to avoid excessive memory usage. */
+ RE_FreePersistentData(NULL);
- engine->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
- DEG_debug_name_set(engine->depsgraph, "RENDER");
+ /* Create new depsgraph if not cached with persistent data. */
+ engine->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
+ DEG_debug_name_set(engine->depsgraph, "RENDER");
+ }
if (engine->re->r.scemode & R_BUTS_PREVIEW) {
+ /* Update for preview render. */
Depsgraph *depsgraph = engine->depsgraph;
DEG_graph_relations_update(depsgraph);
+
+ /* Need GPU context since this might free GPU buffers. */
+ const bool use_gpu_context = (engine->type->flag & RE_USE_GPU_CONTEXT) && reuse_depsgraph;
+ if (use_gpu_context) {
+ DRW_render_context_enable(engine->re);
+ }
+
DEG_evaluate_on_framechange(depsgraph, CFRA);
- DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true);
- DEG_ids_clear_recalc(bmain, depsgraph);
+
+ if (use_gpu_context) {
+ DRW_render_context_disable(engine->re);
+ }
}
else {
- BKE_scene_graph_update_for_newframe(engine->depsgraph);
+ /* Go through update with full Python callbacks for regular render. */
+ BKE_scene_graph_update_for_newframe_ex(engine->depsgraph, false);
}
engine->has_grease_pencil = DRW_render_check_grease_pencil(engine->depsgraph);
}
-static void engine_depsgraph_free(RenderEngine *engine)
+static void engine_depsgraph_exit(RenderEngine *engine)
{
- DEG_graph_free(engine->depsgraph);
-
- engine->depsgraph = NULL;
+ if (engine->depsgraph) {
+ if (engine_keep_depsgraph(engine)) {
+ /* Clear recalc flags since the engine should have handled the updates for the currently
+ * rendered framed by now. */
+ DEG_ids_clear_recalc(engine->depsgraph, false);
+ }
+ else {
+ /* Free immediately to save memory. */
+ engine_depsgraph_free(engine);
+ }
+ }
}
void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
@@ -634,12 +714,15 @@ void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
return;
}
+ /* Clear recalc flags before update so engine can detect what changed. */
+ DEG_ids_clear_recalc(engine->depsgraph, false);
+
Render *re = engine->re;
double cfra = (double)frame + (double)subframe;
CLAMP(cfra, MINAFRAME, MAXFRAME);
BKE_scene_frame_set(re->scene, cfra);
- BKE_scene_graph_update_for_newframe(engine->depsgraph);
+ BKE_scene_graph_update_for_newframe_ex(engine->depsgraph, false);
BKE_scene_camera_switch_update(re->scene);
}
@@ -670,7 +753,6 @@ bool RE_bake_engine(Render *re,
{
RenderEngineType *type = RE_engines_find(re->r.engine);
RenderEngine *engine;
- bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0;
/* set render info */
re->i.cfra = re->scene->r.cfra;
@@ -729,13 +811,13 @@ bool RE_bake_engine(Render *re,
engine->tile_y = 0;
engine->flag &= ~RE_ENGINE_RENDERING;
+ /* Free depsgraph outside of parts mutex lock, since this locks OpenGL context
+ * while the the UI drawing might also lock the OpenGL context and parts mutex. */
+ engine_depsgraph_free(engine);
BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE);
- /* re->engine becomes zero if user changed active render engine during render */
- if (!persistent_data || !re->engine) {
- RE_engine_free(engine);
- re->engine = NULL;
- }
+ RE_engine_free(engine);
+ re->engine = NULL;
RE_parts_free(re);
BLI_rw_mutex_unlock(&re->partsmutex);
@@ -778,13 +860,14 @@ static void engine_render_view_layer(Render *re,
/* Perform render with engine. */
if (use_engine) {
- if (engine->type->flag & RE_USE_GPU_CONTEXT) {
+ const bool use_gpu_context = (engine->type->flag & RE_USE_GPU_CONTEXT);
+ if (use_gpu_context) {
DRW_render_context_enable(engine->re);
}
engine->type->render(engine, engine->depsgraph);
- if (engine->type->flag & RE_USE_GPU_CONTEXT) {
+ if (use_gpu_context) {
DRW_render_context_disable(engine->re);
}
}
@@ -800,13 +883,12 @@ static void engine_render_view_layer(Render *re,
}
/* Free dependency graph, if engine has not done it already. */
- engine_depsgraph_free(engine);
+ engine_depsgraph_exit(engine);
}
bool RE_engine_render(Render *re, bool do_all)
{
RenderEngineType *type = RE_engines_find(re->r.engine);
- bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0;
/* verify if we can render */
if (!type->render) {
@@ -953,7 +1035,13 @@ bool RE_engine_render(Render *re, bool do_all)
}
/* re->engine becomes zero if user changed active render engine during render */
- if (!persistent_data || !re->engine) {
+ if (!engine_keep_depsgraph(engine) || !re->engine) {
+ /* Free depsgraph outside of parts mutex lock, since this locks OpenGL context
+ * while the the UI drawing might also lock the OpenGL context and parts mutex. */
+ BLI_rw_mutex_unlock(&re->partsmutex);
+ engine_depsgraph_free(engine);
+ BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE);
+
RE_engine_free(engine);
re->engine = NULL;
}
@@ -1023,9 +1111,8 @@ void RE_engine_free_blender_memory(RenderEngine *engine)
*
* TODO(sergey): Find better solution for this.
*/
- if (engine->has_grease_pencil) {
+ if (engine->has_grease_pencil || engine_keep_depsgraph(engine)) {
return;
}
- DEG_graph_free(engine->depsgraph);
- engine->depsgraph = NULL;
+ engine_depsgraph_free(engine);
}
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c
index 92bec9c6fd4..a39214b609d 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.c
@@ -662,17 +662,6 @@ void RE_FreeAllRender(void)
#endif
}
-void RE_FreeAllPersistentData(void)
-{
- Render *re;
- for (re = RenderGlobal.renderlist.first; re != NULL; re = re->next) {
- if ((re->r.mode & R_PERSISTENT_DATA) != 0 && re->engine != NULL) {
- RE_engine_free(re->engine);
- re->engine = NULL;
- }
- }
-}
-
/* on file load, free all re */
void RE_FreeAllRenderResults(void)
{
@@ -687,19 +676,39 @@ void RE_FreeAllRenderResults(void)
}
}
-void RE_FreePersistentData(void)
+void RE_FreeAllPersistentData(void)
{
Render *re;
+ for (re = RenderGlobal.renderlist.first; re != NULL; re = re->next) {
+ if (re->engine != NULL) {
+ BLI_assert(!(re->engine->flag & RE_ENGINE_RENDERING));
+ RE_engine_free(re->engine);
+ re->engine = NULL;
+ }
+ }
+}
- /* render engines can be kept around for quick re-render, this clears all */
- for (re = RenderGlobal.renderlist.first; re; re = re->next) {
- if (re->engine) {
- /* if engine is currently rendering, just tag it to be freed when render is finished */
- if (!(re->engine->flag & RE_ENGINE_RENDERING)) {
- RE_engine_free(re->engine);
- }
+static void re_free_persistent_data(Render *re)
+{
+ /* If engine is currently rendering, just wait for it to be freed when it finishes rendering. */
+ if (re->engine && !(re->engine->flag & RE_ENGINE_RENDERING)) {
+ RE_engine_free(re->engine);
+ re->engine = NULL;
+ }
+}
- re->engine = NULL;
+void RE_FreePersistentData(const Scene *scene)
+{
+ /* Render engines can be kept around for quick re-render, this clears all or one scene. */
+ if (scene) {
+ Render *re = RE_GetSceneRender(scene);
+ if (re) {
+ re_free_persistent_data(re);
+ }
+ }
+ else {
+ for (Render *re = RenderGlobal.renderlist.first; re; re = re->next) {
+ re_free_persistent_data(re);
}
}
}
@@ -732,7 +741,7 @@ static void re_init_resolution(Render *re, Render *source, int winx, int winy, r
re->winy = winy;
if (source && (source->r.mode & R_BORDER)) {
/* eeh, doesn't seem original bordered disprect is storing anywhere
- * after insertion on black happening in do_render(),
+ * after insertion on black happening in do_render_engine(),
* so for now simply re-calculate disprect using border from source
* renderer (sergey)
*/
@@ -884,82 +893,6 @@ void RE_InitState(Render *re,
RE_point_density_fix_linking();
}
-/* This function is only called by view3d rendering, which doesn't support
- * multiview at the moment. so handle only one view here */
-static void render_result_rescale(Render *re)
-{
- RenderResult *result = re->result;
- RenderView *rv;
- int x, y;
- float scale_x, scale_y;
- float *src_rectf;
-
- rv = RE_RenderViewGetById(result, 0);
- src_rectf = rv->rectf;
-
- if (src_rectf == NULL) {
- RenderLayer *rl = render_get_active_layer(re, re->result);
- if (rl != NULL) {
- src_rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, NULL);
- }
- }
-
- if (src_rectf != NULL) {
- float *dst_rectf = NULL;
- 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;
- if (dst_rectf == NULL) {
- RenderLayer *rl;
- rl = render_get_active_layer(re, re->result);
- if (rl != NULL) {
- dst_rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, NULL);
- }
- }
-
- scale_x = (float)result->rectx / re->result->rectx;
- scale_y = (float)result->recty / re->result->recty;
- for (x = 0; x < re->result->rectx; x++) {
- for (y = 0; y < re->result->recty; y++) {
- int src_x = x * scale_x;
- int src_y = y * scale_y;
- int dst_index = y * re->result->rectx + x;
- int src_index = src_y * result->rectx + src_x;
- copy_v4_v4(dst_rectf + dst_index * 4, src_rectf + src_index * 4);
- }
- }
- }
- render_result_free(result);
- }
-}
-
-void RE_ChangeResolution(Render *re, int winx, int winy, rcti *disprect)
-{
- re_init_resolution(re, NULL, winx, winy, disprect);
- RE_parts_clamp(re);
-
- if (re->result) {
- BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
- render_result_rescale(re);
- BLI_rw_mutex_unlock(&re->resultmutex);
- }
-}
-
-/* TODO(sergey): This is a bit hackish, used to temporary disable freestyle when
- * doing viewport render. Needs some better integration of BI viewport rendering
- * into the pipeline.
- */
-void RE_ChangeModeFlag(Render *re, int flag, bool clear)
-{
- if (clear) {
- re->r.mode &= ~flag;
- }
- else {
- re->r.mode |= flag;
- }
-}
-
/* update some variables that can be animated, and otherwise wouldn't be due to
* RenderData getting copied once at the start of animation render */
void render_update_anim_renderdata(Render *re, RenderData *rd, ListBase *render_layers)
@@ -1068,24 +1001,8 @@ void *RE_gpu_context_get(Render *re)
return re->gpu_context;
}
-/* ********* add object data (later) ******** */
-
-/* object is considered fully prepared on correct time etc */
-/* includes lights */
-#if 0
-void RE_AddObject(Render *UNUSED(re), Object *UNUSED(ob))
-{
-}
-#endif
-
/* ************ This part uses API, for rendering Blender scenes ********** */
-static void do_render_3d(Render *re)
-{
- re->current_scene_update(re->suh, re->scene);
- RE_engine_render(re, false);
-}
-
/* make sure disprect is not affected by the render border */
static void render_result_disprect_to_full_resolution(Render *re)
{
@@ -1146,8 +1063,8 @@ static void render_result_uncrop(Render *re)
}
}
-/* main render routine, no compositing */
-static void do_render(Render *re)
+/* Render scene into render result, with a render engine. */
+static void do_render_engine(Render *re)
{
Object *camera = RE_GetCamera(re);
/* also check for camera here */
@@ -1160,16 +1077,16 @@ static void do_render(Render *re)
/* now use renderdata and camera to set viewplane */
RE_SetCamera(re, camera);
- do_render_3d(re);
+ re->current_scene_update(re->suh, re->scene);
+ RE_engine_render(re, false);
/* when border render, check if we have to insert it in black */
render_result_uncrop(re);
}
-/* within context of current Render *re, render another scene.
- * it uses current render image size and disprect, but doesn't execute composite
- */
-static void render_scene(Render *re, Scene *sce, int cfra)
+/* Render scene into render result, within a compositor node tree.
+ * Uses the same image dimensions, does not recursively perform compositing. */
+static void do_render_compositor_scene(Render *re, Scene *sce, int cfra)
{
Render *resc = RE_NewSceneRender(sce);
int winx = re->winx, winy = re->winy;
@@ -1204,12 +1121,12 @@ static void render_scene(Render *re, Scene *sce, int cfra)
resc->current_scene_update = re->current_scene_update;
resc->suh = re->suh;
- do_render(resc);
+ do_render_engine(resc);
}
/* helper call to detect if this scene needs a render,
* or if there's a any render layer to render. */
-static int composite_needs_render(Scene *sce, int this_scene)
+static int compositor_needs_render(Scene *sce, int this_scene)
{
bNodeTree *ntree = sce->nodetree;
bNode *node;
@@ -1234,19 +1151,8 @@ static int composite_needs_render(Scene *sce, int this_scene)
return 0;
}
-bool RE_allow_render_generic_object(Object *ob)
-{
- /* override not showing object when duplis are used with particles */
- if (ob->transflag & OB_DUPLIPARTS) {
- /* pass */ /* let particle system(s) handle showing vs. not showing */
- }
- else if (ob->transflag & OB_DUPLI) {
- return false;
- }
- return true;
-}
-
-static void ntree_render_scenes(Render *re)
+/* Render all scenes within a compositor node tree. */
+static void do_render_compositor_scenes(Render *re)
{
bNode *node;
int cfra = re->scene->r.cfra;
@@ -1265,7 +1171,7 @@ static void ntree_render_scenes(Render *re)
Scene *scene = (Scene *)node->id;
if (!BLI_gset_haskey(scenes_rendered, scene) &&
render_scene_has_layers_to_render(scene, false)) {
- render_scene(re, scene, cfra);
+ do_render_compositor_scene(re, scene, cfra);
BLI_gset_add(scenes_rendered, scene);
nodeUpdate(restore_scene->nodetree, node);
}
@@ -1276,7 +1182,7 @@ static void ntree_render_scenes(Render *re)
}
/* bad call... need to think over proper method still */
-static void render_composit_stats(void *arg, const char *str)
+static void render_compositor_stats(void *arg, const char *str)
{
Render *re = (Render *)arg;
@@ -1286,13 +1192,14 @@ static void render_composit_stats(void *arg, const char *str)
re->stats_draw(re->sdh, &i);
}
-/* returns fully composited render-result on given time step (in RenderData) */
-static void do_render_composite(Render *re)
+/* Render compositor nodes, along with any scenes required for them.
+ * The result will be output into a compositing render layer in the render result. */
+static void do_render_compositor(Render *re)
{
bNodeTree *ntree = re->pipeline_scene_eval->nodetree;
int update_newframe = 0;
- if (composite_needs_render(re->pipeline_scene_eval, 1)) {
+ if (compositor_needs_render(re->pipeline_scene_eval, 1)) {
/* save memory... free all cached images */
ntreeFreeCache(ntree);
@@ -1300,7 +1207,7 @@ static void do_render_composite(Render *re)
* it could be optimized to render only the needed view
* but what if a scene has a different number of views
* than the main scene? */
- do_render(re);
+ do_render_engine(re);
}
else {
re->i.cfra = re->r.cfra;
@@ -1336,11 +1243,11 @@ static void do_render_composite(Render *re)
if (ntree && re->scene->use_nodes && re->r.scemode & R_DOCOMP) {
/* checks if there are render-result nodes that need scene */
if ((re->r.scemode & R_SINGLE_LAYER) == 0) {
- ntree_render_scenes(re);
+ do_render_compositor_scenes(re);
}
if (!re->test_break(re->tbh)) {
- ntree->stats_draw = render_composit_stats;
+ ntree->stats_draw = render_compositor_stats;
ntree->test_break = re->test_break;
ntree->progress = re->progress;
ntree->sdh = re;
@@ -1423,7 +1330,8 @@ int RE_seq_render_active(Scene *scene, RenderData *rd)
return 0;
}
-static void do_render_seq(Render *re)
+/* Render sequencer strips into render result. */
+static void do_render_sequencer(Render *re)
{
static int recurs_depth = 0;
struct ImBuf *out;
@@ -1537,8 +1445,8 @@ static void do_render_seq(Render *re)
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
-/* main loop: doing sequence + 3d render + compositing */
-static void do_render_all_options(Render *re)
+/* Render full pipeline, using render engine, sequencer and compositing nodes. */
+static void do_render_full_pipeline(Render *re)
{
bool render_seq = false;
@@ -1556,9 +1464,9 @@ static void do_render_all_options(Render *re)
/* in this case external render overrides all */
}
else if (RE_seq_render_active(re->scene, &re->r)) {
- /* note: do_render_seq() frees rect32 when sequencer returns float images */
+ /* note: do_render_sequencer() frees rect32 when sequencer returns float images */
if (!re->test_break(re->tbh)) {
- do_render_seq(re);
+ do_render_sequencer(re);
render_seq = true;
}
@@ -1566,7 +1474,7 @@ static void do_render_all_options(Render *re)
re->display_update(re->duh, re->result, NULL);
}
else {
- do_render_composite(re);
+ do_render_compositor(re);
}
re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime;
@@ -1701,7 +1609,7 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
return true;
}
-static bool node_tree_has_composite_output(bNodeTree *ntree)
+static bool node_tree_has_compositor_output(bNodeTree *ntree)
{
bNode *node;
@@ -1711,7 +1619,7 @@ static bool node_tree_has_composite_output(bNodeTree *ntree)
}
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
if (node->id) {
- if (node_tree_has_composite_output((bNodeTree *)node->id)) {
+ if (node_tree_has_compositor_output((bNodeTree *)node->id)) {
return true;
}
}
@@ -1721,9 +1629,9 @@ static bool node_tree_has_composite_output(bNodeTree *ntree)
return false;
}
-static int check_composite_output(Scene *scene)
+static int check_compositor_output(Scene *scene)
{
- return node_tree_has_composite_output(scene->nodetree);
+ return node_tree_has_compositor_output(scene->nodetree);
}
bool RE_is_rendering_allowed(Scene *scene,
@@ -1766,13 +1674,13 @@ bool RE_is_rendering_allowed(Scene *scene,
return 0;
}
- if (!check_composite_output(scene)) {
+ if (!check_compositor_output(scene)) {
BKE_report(reports, RPT_ERROR, "No render output node in scene");
return 0;
}
if (scemode & R_FULL_SAMPLE) {
- if (composite_needs_render(scene, 0) == 0) {
+ if (compositor_needs_render(scene, 0) == 0) {
BKE_report(reports, RPT_ERROR, "Full sample AA not supported without 3D rendering");
return 0;
}
@@ -1941,6 +1849,22 @@ static void render_init_depsgraph(Render *re)
re->pipeline_scene_eval = DEG_get_evaluated_scene(re->pipeline_depsgraph);
}
+/* Free data only needed during rendering operation. */
+static void render_pipeline_free(Render *re)
+{
+ if (re->engine && !RE_engine_use_persistent_data(re->engine)) {
+ RE_engine_free(re->engine);
+ re->engine = NULL;
+ }
+ if (re->pipeline_depsgraph != NULL) {
+ DEG_graph_free(re->pipeline_depsgraph);
+ re->pipeline_depsgraph = NULL;
+ re->pipeline_scene_eval = NULL;
+ }
+ /* Destroy the opengl context in the correct thread. */
+ RE_gl_context_destroy(re);
+}
+
/* general Blender frame render call */
void RE_RenderFrame(Render *re,
Main *bmain,
@@ -1966,7 +1890,7 @@ void RE_RenderFrame(Render *re,
render_init_depsgraph(re);
- do_render_all_options(re);
+ do_render_full_pipeline(re);
if (write_still && !G.is_break) {
if (BKE_imtype_is_movie(rd.im_format.imtype)) {
@@ -2001,7 +1925,7 @@ void RE_RenderFrame(Render *re,
&scene->id,
G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE);
- RE_CleanAfterRender(re);
+ render_pipeline_free(re);
/* UGLY WARNING */
G.is_rendering = false;
@@ -2038,7 +1962,7 @@ void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, int render
change_renderdata_engine(re, RE_engine_id_BLENDER_EEVEE);
}
- do_render_3d(re);
+ RE_engine_render(re, false);
change_renderdata_engine(re, scene_engine);
}
@@ -2444,7 +2368,7 @@ void RE_RenderAnim(Render *re,
if (is_error) {
/* report is handled above */
re_movie_free_all(re, mh, i + 1);
- RE_CleanAfterRender(re);
+ render_pipeline_free(re);
return;
}
}
@@ -2568,7 +2492,7 @@ void RE_RenderAnim(Render *re,
/* run callbacks before rendering, before the scene is updated */
render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_PRE);
- do_render_all_options(re);
+ do_render_full_pipeline(re);
totrendered++;
if (re->test_break(re->tbh) == 0) {
@@ -2642,7 +2566,7 @@ void RE_RenderAnim(Render *re,
G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE);
BKE_sound_reset_scene_specs(re->pipeline_scene_eval);
- RE_CleanAfterRender(re);
+ render_pipeline_free(re);
/* UGLY WARNING */
G.is_rendering = false;
@@ -2664,18 +2588,13 @@ void RE_PreviewRender(Render *re, Main *bmain, Scene *sce)
camera = RE_GetCamera(re);
RE_SetCamera(re, camera);
- do_render_3d(re);
-}
+ RE_engine_render(re, false);
-void RE_CleanAfterRender(Render *re)
-{
- /* Destroy the opengl context in the correct thread. */
- RE_gl_context_destroy(re);
- if (re->pipeline_depsgraph != NULL) {
- DEG_graph_free(re->pipeline_depsgraph);
+ /* No persistent data for preview render. */
+ if (re->engine) {
+ RE_engine_free(re->engine);
+ re->engine = NULL;
}
- re->pipeline_depsgraph = NULL;
- re->pipeline_scene_eval = NULL;
}
/* note; repeated win/disprect calc... solve that nicer, also in compo */
@@ -2917,3 +2836,15 @@ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const cha
/* create a totally new pass */
return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA");
}
+
+bool RE_allow_render_generic_object(Object *ob)
+{
+ /* override not showing object when duplis are used with particles */
+ if (ob->transflag & OB_DUPLIPARTS) {
+ /* pass */ /* let particle system(s) handle showing vs. not showing */
+ }
+ else if (ob->transflag & OB_DUPLI) {
+ return false;
+ }
+ return true;
+}
diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c
index f73a200f3c6..74c96392d48 100644
--- a/source/blender/render/intern/render_result.c
+++ b/source/blender/render/intern/render_result.c
@@ -126,7 +126,7 @@ void render_result_free(RenderResult *rr)
MEM_freeN(rr);
}
-/* version that's compatible with fullsample buffers */
+/** Version that's compatible with full-sample buffers. */
void render_result_free_list(ListBase *lb, RenderResult *rr)
{
RenderResult *rrnext;
diff --git a/source/blender/render/intern/render_types.h b/source/blender/render/intern/render_types.h
index 0488bf6e87a..9f399971049 100644
--- a/source/blender/render/intern/render_types.h
+++ b/source/blender/render/intern/render_types.h
@@ -79,7 +79,7 @@ struct Render {
RenderResult *result;
/* if render with single-layer option, other rendered layers are stored here */
RenderResult *pushedresult;
- /* a list of RenderResults, for fullsample */
+ /** A list of #RenderResults, for full-samples. */
ListBase fullresult;
/* read/write mutex, all internal code that writes to re->result must use a
* write lock, all external code must use a read lock. internal code is assumed
diff --git a/source/blender/render/intern/zbuf.c b/source/blender/render/intern/zbuf.c
index 33af3bbaf29..242c8a199fb 100644
--- a/source/blender/render/intern/zbuf.c
+++ b/source/blender/render/intern/zbuf.c
@@ -175,7 +175,7 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2])
/* Functions */
/*-----------------------------------------------------------*/
-/* Scanconvert for strand triangles, calls func for each x, y coordinate
+/* Scan-convert for strand triangles, calls function for each x, y coordinate
* and gives UV barycentrics and z. */
void zspan_scanconvert(ZSpan *zspan,
diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt
index 9d9553ed858..2b402b4750f 100644
--- a/source/blender/sequencer/CMakeLists.txt
+++ b/source/blender/sequencer/CMakeLists.txt
@@ -72,9 +72,9 @@ set(SRC
intern/multiview.h
intern/prefetch.c
intern/prefetch.h
- intern/proxy_job.c
intern/proxy.c
intern/proxy.h
+ intern/proxy_job.c
intern/render.c
intern/render.h
intern/sequencer.c
diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h
index 9cb52145c04..2941eb6f4c0 100644
--- a/source/blender/sequencer/SEQ_add.h
+++ b/source/blender/sequencer/SEQ_add.h
@@ -36,6 +36,7 @@ typedef enum eSeqLoadFlags {
SEQ_LOAD_SOUND_CACHE = (1 << 1),
SEQ_LOAD_SOUND_MONO = (1 << 2),
SEQ_LOAD_MOVIE_SYNC_FPS = (1 << 3),
+ SEQ_LOAD_SET_VIEW_TRANSFORM = (1 << 4),
} eSeqLoadFlags;
/* Api for adding new sequence strips. */
diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h
index 44908dca85a..c7c2dc275ee 100644
--- a/source/blender/sequencer/SEQ_iterator.h
+++ b/source/blender/sequencer/SEQ_iterator.h
@@ -27,48 +27,70 @@
extern "C" {
#endif
+#include "BLI_ghash.h"
+
struct Editing;
struct Sequence;
+struct GSet;
+struct GSetIterator;
-typedef struct SeqIterator {
- struct Sequence **array;
- int tot, cur;
+#define SEQ_ITERATOR_FOREACH(var, collection) \
+ for (SeqIterator iter = {{{NULL}}}; \
+ SEQ_iterator_ensure(collection, &iter, &var) && var != NULL; \
+ var = SEQ_iterator_yield(&iter))
- struct Sequence *seq;
- int valid;
-} SeqIterator;
-
-#define SEQ_ALL_BEGIN(ed, _seq) \
+#define SEQ_ALL_BEGIN(ed, var) \
{ \
- SeqIterator iter_macro; \
- for (SEQ_iterator_begin(ed, &iter_macro, false); iter_macro.valid; \
- SEQ_iterator_next(&iter_macro)) { \
- _seq = iter_macro.seq;
+ if (ed != NULL) { \
+ SeqCollection *all_strips = SEQ_query_all_strips_recursive(&ed->seqbase); \
+ GSetIterator gsi; \
+ GSET_ITER (gsi, all_strips->set) { \
+ var = (Sequence *)(BLI_gsetIterator_getKey(&gsi));
#define SEQ_ALL_END \
} \
- SEQ_iterator_end(&iter_macro); \
+ SEQ_collection_free(all_strips); \
+ } \
} \
((void)0)
-#define SEQ_CURRENT_BEGIN(_ed, _seq) \
- { \
- SeqIterator iter_macro; \
- for (SEQ_iterator_begin(_ed, &iter_macro, true); iter_macro.valid; \
- SEQ_iterator_next(&iter_macro)) { \
- _seq = iter_macro.seq;
+typedef struct SeqCollection {
+ struct SeqCollection *next, *prev;
+ struct GSet *set;
+} SeqCollection;
+
+typedef struct SeqIterator {
+ GSetIterator gsi;
+ SeqCollection *collection;
+ bool iterator_initialized;
+} SeqIterator;
-#define SEQ_CURRENT_END SEQ_ALL_END
+bool SEQ_iterator_ensure(SeqCollection *collection,
+ SeqIterator *iterator,
+ struct Sequence **r_seq);
+struct Sequence *SEQ_iterator_yield(SeqIterator *iterator);
-void SEQ_iterator_begin(struct Editing *ed, SeqIterator *iter, const bool use_current_sequences);
-void SEQ_iterator_next(SeqIterator *iter);
-void SEQ_iterator_end(SeqIterator *iter);
-int SEQ_iterator_seqbase_recursive_apply(struct ListBase *seqbase,
- int (*apply_fn)(struct Sequence *seq, void *),
- void *arg);
-int SEQ_iterator_recursive_apply(struct Sequence *seq,
- int (*apply_fn)(struct Sequence *, void *),
- void *arg);
+SeqCollection *SEQ_collection_create(void);
+bool SEQ_collection_append_strip(struct Sequence *seq, SeqCollection *data);
+bool SEQ_collection_remove_strip(struct Sequence *seq, SeqCollection *data);
+void SEQ_collection_free(SeqCollection *collection);
+void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src);
+void SEQ_collection_expand(struct ListBase *seqbase,
+ SeqCollection *collection,
+ void query_func(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection));
+SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ void seq_query_func(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection));
+SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase);
+SeqCollection *SEQ_query_all_strips(ListBase *seqbase);
+SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase);
+void SEQ_query_strip_effect_chain(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 85513faf3e6..ad0815892f7 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -44,16 +44,13 @@ enum {
SEQ_SIDE_NO_CHANGE,
};
-#define SEQ_CACHE_COST_MAX 10.0f
-
/* seq_dupli' flags */
#define SEQ_DUPE_UNIQUE_NAME (1 << 0)
-#define SEQ_DUPE_CONTEXT (1 << 1)
-#define SEQ_DUPE_ANIM (1 << 2)
#define SEQ_DUPE_ALL (1 << 3) /* otherwise only selected are copied */
#define SEQ_DUPE_IS_RECURSIVE_CALL (1 << 4)
struct SequencerToolSettings *SEQ_tool_settings_init(void);
+struct SequencerToolSettings *SEQ_tool_settings_ensure(struct Scene *scene);
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);
diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h
index 31549ff3994..67d3a2e5960 100644
--- a/source/blender/sequencer/SEQ_time.h
+++ b/source/blender/sequencer/SEQ_time.h
@@ -45,6 +45,7 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene,
void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq);
void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq);
int SEQ_time_cmp_time_startdisp(const void *a, const void *b);
+bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h
index d448fbdb43e..d587bd0f1a1 100644
--- a/source/blender/sequencer/SEQ_transform.h
+++ b/source/blender/sequencer/SEQ_transform.h
@@ -31,8 +31,8 @@ struct ListBase;
struct Scene;
struct Sequence;
-int SEQ_transform_get_left_handle_frame(struct Sequence *seq, bool metaclip);
-int SEQ_transform_get_right_handle_frame(struct Sequence *seq, bool metaclip);
+int SEQ_transform_get_left_handle_frame(struct Sequence *seq);
+int SEQ_transform_get_right_handle_frame(struct Sequence *seq);
void SEQ_transform_set_left_handle_frame(struct Sequence *seq, int val);
void SEQ_transform_set_right_handle_frame(struct Sequence *seq, int val);
void SEQ_transform_handle_xlimits(struct Sequence *seq, int leftflag, int rightflag);
diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h
index 45f53a64688..a4dc80d75db 100644
--- a/source/blender/sequencer/SEQ_utils.h
+++ b/source/blender/sequencer/SEQ_utils.h
@@ -35,7 +35,7 @@ struct Scene;
struct Sequence;
struct StripElem;
-void SEQ_sort(struct Scene *scene);
+void SEQ_sort(struct ListBase *seqbase);
void SEQ_sequence_base_unique_name_recursive(struct ListBase *seqbasep, struct Sequence *seq);
const char *SEQ_sequence_give_name(struct Sequence *seq);
struct ListBase *SEQ_get_seqbase_from_sequence(struct Sequence *seq, int *r_offset);
@@ -54,6 +54,13 @@ void SEQ_set_scale_to_fit(const struct Sequence *seq,
const int preview_width,
const int preview_height,
const eSeqImageFitMethod fit_method);
+int SEQ_seqbase_recursive_apply(struct ListBase *seqbase,
+ int (*apply_fn)(struct Sequence *seq, void *),
+ void *arg);
+int SEQ_recursive_apply(struct Sequence *seq,
+ int (*apply_fn)(struct Sequence *, void *),
+ void *arg);
+void SEQ_ensure_unique_name(struct Sequence *seq, struct Scene *scene);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 278320d873e..d41a2c19d55 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -3033,10 +3033,9 @@ static ImBuf *do_adjustment_impl(const SeqRenderData *context, Sequence *seq, fl
i = seq_render_give_ibuf_seqbase(context, timeline_frame, seq->machine - 1, seqbasep);
}
- /* found nothing? so let's work the way up the metastrip stack, so
+ /* Found nothing? so let's work the way up the meta-strip stack, so
* that it is possible to group a bunch of adjustment strips into
- * a metastrip and have that work on everything below the metastrip
- */
+ * a meta-strip and have that work on everything below the meta-strip. */
if (!i) {
Sequence *meta;
diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c
index 290ee185865..a6e4b6ea7ed 100644
--- a/source/blender/sequencer/intern/image_cache.c
+++ b/source/blender/sequencer/intern/image_cache.c
@@ -35,6 +35,7 @@
#include "IMB_imbuf_types.h"
#include "BLI_blenlib.h"
+#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_fileops.h"
#include "BLI_fileops_types.h"
@@ -44,7 +45,6 @@
#include "BLI_path_util.h"
#include "BLI_threads.h"
-#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -352,15 +352,18 @@ static void seq_disk_cache_update_file(SeqDiskCache *disk_cache, char *path)
}
/* Path format:
- * <cache dir>/<project name>/<scene name>-<timestamp>/<seq name>/DCACHE_FNAME_FORMAT
+ * <cache dir>/<project name>_seq_cache/<scene name>-<timestamp>/<seq name>/DCACHE_FNAME_FORMAT
*/
static void seq_disk_cache_get_project_dir(SeqDiskCache *disk_cache, char *path, size_t path_len)
{
- char main_name[FILE_MAX];
- BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), main_name, sizeof(main_name));
+ char cache_dir[FILE_MAX];
+ BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), cache_dir, sizeof(cache_dir));
+ /* Use suffix, so that the cache directory name does not conflict with the bmain's blend file. */
+ const char *suffix = "_seq_cache";
+ strncat(cache_dir, suffix, sizeof(cache_dir) - strlen(cache_dir) - 1);
BLI_strncpy(path, seq_disk_cache_base_dir(), path_len);
- BLI_path_append(path, path_len, main_name);
+ BLI_path_append(path, path_len, cache_dir);
}
static void seq_disk_cache_get_dir(
@@ -421,7 +424,7 @@ static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache)
BLI_strncpy(path_version_file, path, sizeof(path_version_file));
BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version");
- if (BLI_exists(path)) {
+ if (BLI_exists(path) && BLI_is_dir(path)) {
FILE *file = BLI_fopen(path_version_file, "r");
if (file) {
@@ -515,7 +518,7 @@ static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntr
static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header)
{
- fseek(file, 0, 0);
+ BLI_fseek(file, 0LL, SEEK_SET);
const size_t num_items_read = fread(header, sizeof(*header), 1, file);
if (num_items_read < 1) {
BLI_assert(!"unable to read disk cache header");
@@ -537,7 +540,7 @@ static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header)
static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header)
{
- fseek(file, 0, 0);
+ BLI_fseek(file, 0LL, SEEK_SET);
return fwrite(header, sizeof(*header), 1, file);
}
@@ -976,14 +979,34 @@ static void seq_cache_recycle_linked(Scene *scene, SeqCacheKey *base)
SeqCacheKey *next = base->link_next;
while (base) {
+ if (!BLI_ghash_haskey(cache->hash, base)) {
+ break; /* Key has already been removed from cache. */
+ }
+
SeqCacheKey *prev = base->link_prev;
+ if (prev != NULL && prev->link_next != base) {
+ /* Key has been removed and replaced and doesn't belong to this chain anymore. */
+ base->link_prev = NULL;
+ break;
+ }
+
BLI_ghash_remove(cache->hash, base, seq_cache_keyfree, seq_cache_valfree);
base = prev;
}
base = next;
while (base) {
+ if (!BLI_ghash_haskey(cache->hash, base)) {
+ break; /* Key has already been removed from cache. */
+ }
+
next = base->link_next;
+ if (next != NULL && next->link_prev != base) {
+ /* Key has been removed and replaced and doesn't belong to this chain anymore. */
+ base->link_next = NULL;
+ break;
+ }
+
BLI_ghash_remove(cache->hash, base, seq_cache_keyfree, seq_cache_valfree);
base = next;
}
diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c
index f99667dea04..9bbc5362f18 100644
--- a/source/blender/sequencer/intern/iterator.c
+++ b/source/blender/sequencer/intern/iterator.c
@@ -31,138 +31,268 @@
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
+#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BKE_scene.h"
#include "SEQ_iterator.h"
-/* ************************* iterator ************************** */
-/* *************** (replaces old WHILE_SEQ) ********************* */
-/* **************** use now SEQ_ALL_BEGIN () SEQ_ALL_END ***************** */
+/* -------------------------------------------------------------------- */
+/** \Iterator API
+ * \{ */
-/* sequence strip iterator:
- * - builds a full array, recursively into meta strips
+/**
+ * Utility function for SEQ_ITERATOR_FOREACH macro.
+ * Ensure, that iterator is initialized. During initialization return pointer to collection element
+ * and step gset iterator. When this function is called after iterator has been initialized, it
+ * will do nothing and return true.
+ *
+ * \param collection: collection to iterate
+ * \param iterator: iterator to be initialized
+ * \param r_seq: pointer to Sequence pointer
+ *
+ * \return false when iterator can not be initialized, true otherwise
*/
-
-static void seq_count(ListBase *seqbase, int *tot)
+bool SEQ_iterator_ensure(SeqCollection *collection, SeqIterator *iterator, Sequence **r_seq)
{
- Sequence *seq;
-
- for (seq = seqbase->first; seq; seq = seq->next) {
- (*tot)++;
-
- if (seq->seqbase.first) {
- seq_count(&seq->seqbase, tot);
- }
+ if (iterator->iterator_initialized) {
+ return true;
}
-}
-static void seq_build_array(ListBase *seqbase, Sequence ***array, int depth)
-{
- Sequence *seq;
+ if (BLI_gset_len(collection->set) == 0) {
+ return false;
+ }
- for (seq = seqbase->first; seq; seq = seq->next) {
- seq->depth = depth;
+ iterator->collection = collection;
+ BLI_gsetIterator_init(&iterator->gsi, iterator->collection->set);
+ iterator->iterator_initialized = true;
- if (seq->seqbase.first) {
- seq_build_array(&seq->seqbase, array, depth + 1);
- }
+ *r_seq = BLI_gsetIterator_getKey(&iterator->gsi);
+ BLI_gsetIterator_step(&iterator->gsi);
- **array = seq;
- (*array)++;
- }
+ return true;
}
-static void seq_array(Editing *ed,
- const bool use_current_sequences,
- Sequence ***r_seqarray,
- int *r_seqarray_len)
+/**
+ * Utility function for SEQ_ITERATOR_FOREACH macro.
+ * Yield collection element
+ *
+ * \param iterator: iterator to be initialized
+ *
+ * \return collection element or NULL when iteration has ended
+ */
+Sequence *SEQ_iterator_yield(SeqIterator *iterator)
{
- Sequence **array;
+ Sequence *seq = BLI_gsetIterator_done(&iterator->gsi) ? NULL :
+ BLI_gsetIterator_getKey(&iterator->gsi);
+ BLI_gsetIterator_step(&iterator->gsi);
+ return seq;
+}
- *r_seqarray = NULL;
- *r_seqarray_len = 0;
+/**
+ * Free strip collection.
+ *
+ * \param collection: collection to be freed
+ */
+void SEQ_collection_free(SeqCollection *collection)
+{
+ BLI_gset_free(collection->set, NULL);
+ MEM_freeN(collection);
+}
- if (ed == NULL) {
- return;
- }
+/**
+ * Create new empty strip collection.
+ *
+ * \return empty strip collection.
+ */
+SeqCollection *SEQ_collection_create(void)
+{
+ SeqCollection *collection = MEM_callocN(sizeof(SeqCollection), "SeqCollection");
+ collection->set = BLI_gset_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "SeqCollection GSet");
+ return collection;
+}
- if (use_current_sequences) {
- seq_count(ed->seqbasep, r_seqarray_len);
- }
- else {
- seq_count(&ed->seqbase, r_seqarray_len);
+/**
+ * Query strips from seqbase. seq_reference is used by query function as filter condition.
+ *
+ * \param seq_reference: reference strip for query function
+ * \param seqbase: ListBase in which strips are queried
+ * \param seq_query_func: query function callback
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_by_reference(Sequence *seq_reference,
+ ListBase *seqbase,
+ void seq_query_func(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection))
+{
+ SeqCollection *collection = SEQ_collection_create();
+ seq_query_func(seq_reference, seqbase, collection);
+ return collection;
+}
+/**
+ * Add strip to collection.
+ *
+ * \param seq: strip to be added
+ * \param collection: collection to which strip will be added
+ * \return false if strip is already in set, otherwise true
+ */
+bool SEQ_collection_append_strip(Sequence *seq, SeqCollection *collection)
+{
+ if (BLI_gset_lookup(collection->set, seq) != NULL) {
+ return false;
}
+ BLI_gset_insert(collection->set, seq);
+ return true;
+}
- if (*r_seqarray_len == 0) {
- return;
- }
+/**
+ * Remove strip from collection.
+ *
+ * \param seq: strip to be removed
+ * \param collection: collection from which strip will be removed
+ * \return true if strip exists in set and it was removed from set, otherwise false
+ */
+bool SEQ_collection_remove_strip(Sequence *seq, SeqCollection *collection)
+{
+ return BLI_gset_remove(collection->set, seq, NULL);
+}
- *r_seqarray = array = MEM_mallocN(sizeof(Sequence *) * (*r_seqarray_len), "SeqArray");
- if (use_current_sequences) {
- seq_build_array(ed->seqbasep, &array, 0);
- }
- else {
- seq_build_array(&ed->seqbase, &array, 0);
+/**
+ * Move strips from collection_src to collection_dst. Source collection will be freed.
+ *
+ * \param collection_dst: destination collection
+ * \param collection_src: source collection
+ */
+void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection_src) {
+ SEQ_collection_append_strip(seq, collection_dst);
}
+ SEQ_collection_free(collection_src);
}
-void SEQ_iterator_begin(Editing *ed, SeqIterator *iter, const bool use_current_sequences)
+/**
+ * Expand collection by running SEQ_query() for each strip, which will be used as reference.
+ * Results of these queries will be merged into provided collection.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \param collection: SeqCollection to be expanded
+ * \param seq_query_func: query function callback
+ */
+void SEQ_collection_expand(ListBase *seqbase,
+ SeqCollection *collection,
+ void seq_query_func(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection))
{
- memset(iter, 0, sizeof(*iter));
- seq_array(ed, use_current_sequences, &iter->array, &iter->tot);
+ /* Collect expanded results for each sequence in provided SeqIteratorCollection. */
+ ListBase expand_collections = {0};
- if (iter->tot) {
- iter->cur = 0;
- iter->seq = iter->array[iter->cur];
- iter->valid = 1;
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ SeqCollection *expand_collection = SEQ_query_by_reference(seq, seqbase, seq_query_func);
+ BLI_addtail(&expand_collections, expand_collection);
+ }
+
+ /* Merge all expanded results in provided SeqIteratorCollection. */
+ LISTBASE_FOREACH_MUTABLE (SeqCollection *, expand_collection, &expand_collections) {
+ BLI_remlink(&expand_collections, expand_collection);
+ SEQ_collection_merge(collection, expand_collection);
}
}
-void SEQ_iterator_next(SeqIterator *iter)
+/** \} */
+
+/**
+ * Query all strips in seqbase and nested meta strips.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase)
{
- if (++iter->cur < iter->tot) {
- iter->seq = iter->array[iter->cur];
- }
- else {
- iter->valid = 0;
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (seq->type == SEQ_TYPE_META) {
+ SEQ_collection_merge(collection, SEQ_query_all_strips_recursive(&seq->seqbase));
+ }
+ SEQ_collection_append_strip(seq, collection);
}
+ return collection;
}
-void SEQ_iterator_end(SeqIterator *iter)
+/**
+ * Query all strips in seqbase. This does not include strips nested in meta strips.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_all_strips(ListBase *seqbase)
{
- if (iter->array) {
- MEM_freeN(iter->array);
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ SEQ_collection_append_strip(seq, collection);
}
-
- iter->valid = 0;
+ return collection;
}
-int SEQ_iterator_seqbase_recursive_apply(ListBase *seqbase,
- int (*apply_fn)(Sequence *seq, void *),
- void *arg)
+/**
+ * Query all selected strips in seqbase.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_selected_strips(ListBase *seqbase)
{
- Sequence *iseq;
- for (iseq = seqbase->first; iseq; iseq = iseq->next) {
- if (SEQ_iterator_recursive_apply(iseq, apply_fn, arg) == -1) {
- return -1; /* bail out */
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if ((seq->flag & SELECT) == 0) {
+ continue;
}
+ SEQ_collection_append_strip(seq, collection);
}
- return 1;
+ return collection;
}
-int SEQ_iterator_recursive_apply(Sequence *seq, int (*apply_fn)(Sequence *, void *), void *arg)
+/**
+ * Query all effect strips that are directly or indirectly connected to seq_reference.
+ * This includes all effects of seq_reference, strips used by another inputs and their effects, so
+ * that whole chain is fully independent of other strips.
+ *
+ * \param seq_reference: reference strip
+ * \param seqbase: ListBase in which strips are queried
+ * \param collection: collection to be filled
+ */
+void SEQ_query_strip_effect_chain(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection)
{
- int ret = apply_fn(seq, arg);
-
- if (ret == -1) {
- return -1; /* bail out */
+ if (!SEQ_collection_append_strip(seq_reference, collection)) {
+ return; /* Strip is already in set, so all effects connected to it are as well. */
}
- if (ret && seq->seqbase.first) {
- ret = SEQ_iterator_seqbase_recursive_apply(&seq->seqbase, apply_fn, arg);
+ /* Find all strips that seq_reference is connected to. */
+ if (seq_reference->type & SEQ_TYPE_EFFECT) {
+ if (seq_reference->seq1) {
+ SEQ_query_strip_effect_chain(seq_reference->seq1, seqbase, collection);
+ }
+ if (seq_reference->seq2) {
+ SEQ_query_strip_effect_chain(seq_reference->seq2, seqbase, collection);
+ }
+ if (seq_reference->seq3) {
+ SEQ_query_strip_effect_chain(seq_reference->seq3, seqbase, collection);
+ }
}
- return ret;
+ /* Find all strips connected to seq_reference. */
+ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
+ if (seq_test->seq1 == seq_reference || seq_test->seq2 == seq_reference ||
+ seq_test->seq3 == seq_reference) {
+ SEQ_query_strip_effect_chain(seq_test, seqbase, collection);
+ }
+ }
}
diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c
index a0e6bc7f4f1..bd7ea5b958c 100644
--- a/source/blender/sequencer/intern/proxy.c
+++ b/source/blender/sequencer/intern/proxy.c
@@ -466,16 +466,18 @@ bool SEQ_proxy_rebuild_context(Main *bmain,
seq_open_anim_file(scene, nseq, true);
sanim = BLI_findlink(&nseq->anims, i);
- context->index_context = IMB_anim_index_rebuild_context(sanim->anim,
- context->tc_flags,
- context->size_flags,
- context->quality,
- context->overwrite,
- file_list);
- }
- if (!context->index_context) {
- SEQ_proxy_rebuild_finish(context, false);
- return false;
+ if (sanim->anim) {
+ context->index_context = IMB_anim_index_rebuild_context(sanim->anim,
+ context->tc_flags,
+ context->size_flags,
+ context->quality,
+ context->overwrite,
+ file_list);
+ }
+ if (!context->index_context) {
+ MEM_freeN(context);
+ return false;
+ }
}
link = BLI_genericNodeN(context);
@@ -585,7 +587,7 @@ void SEQ_proxy_set(struct Sequence *seq, bool value)
seq->flag |= SEQ_USE_PROXY;
if (seq->strip->proxy == NULL) {
seq->strip->proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy");
- seq->strip->proxy->quality = 90;
+ seq->strip->proxy->quality = 50;
seq->strip->proxy->build_tc_flags = SEQ_PROXY_TC_ALL;
seq->strip->proxy->build_size_flags = SEQ_PROXY_IMAGE_SIZE_25;
}
diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c
index 572fff0ad38..b0a8605e922 100644
--- a/source/blender/sequencer/intern/render.c
+++ b/source/blender/sequencer/intern/render.c
@@ -37,6 +37,7 @@
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
+#include "BLI_rect.h"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -65,10 +66,12 @@
#include "RE_pipeline.h"
#include "SEQ_effects.h"
+#include "SEQ_iterator.h"
#include "SEQ_modifier.h"
#include "SEQ_proxy.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "SEQ_utils.h"
#include "effects.h"
@@ -259,120 +262,132 @@ StripElem *SEQ_render_give_stripelem(Sequence *seq, int timeline_frame)
return se;
}
-static int evaluate_seq_frame_gen(Sequence **seq_arr,
- ListBase *seqbase,
- int timeline_frame,
- int chanshown)
+static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input)
{
- /* Use arbitrary sized linked list, the size could be over MAXSEQ. */
- LinkNodePair effect_inputs = {NULL, NULL};
- int totseq = 0;
+ if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input ||
+ seq_effect->seq3 == possibly_input) {
+ return true;
+ }
+ return false;
+}
- memset(seq_arr, 0, sizeof(Sequence *) * (MAXSEQ + 1));
+/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself.
+ * Order of applying these conditions is important. */
+static bool must_render_strip(const Sequence *seq, SeqCollection *strips_under_playhead)
+{
+ bool seq_have_effect_in_stack = false;
+ Sequence *seq_iter;
+ SEQ_ITERATOR_FOREACH (seq_iter, strips_under_playhead) {
+ /* Strips is below another strip with replace blending are not rendered. */
+ if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) {
+ return false;
+ }
- LISTBASE_FOREACH (Sequence *, seq, seqbase) {
- if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) {
- if ((seq->type & SEQ_TYPE_EFFECT) && !(seq->flag & SEQ_MUTE)) {
+ if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) {
+ /* Strips in same channel or higher than its effect are rendered. */
+ if (seq->machine >= seq_iter->machine) {
+ return true;
+ }
+ /* Mark that this strip has effect in stack, that is above the strip. */
+ seq_have_effect_in_stack = true;
+ }
+ }
- if (seq->seq1) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq1);
- }
+ /* All effects are rendered (with respect to conditions above). */
+ if ((seq->type & SEQ_TYPE_EFFECT) != 0) {
+ return true;
+ }
- if (seq->seq2) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq2);
- }
+ /* If strip has effects in stack, and all effects are above this strip, it it not rendered. */
+ if (seq_have_effect_in_stack) {
+ return false;
+ }
- if (seq->seq3) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq3);
- }
- }
+ return true;
+}
- seq_arr[seq->machine] = seq;
- totseq++;
+static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame)
+{
+ SeqCollection *collection = SEQ_collection_create();
+
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
+ SEQ_collection_append_strip(seq, collection);
}
}
+ return collection;
+}
- /* Drop strips which are used for effect inputs, we don't want
- * them to blend into render stack in any other way than effect
- * string rendering. */
- for (LinkNode *seq_item = effect_inputs.list; seq_item; seq_item = seq_item->next) {
- Sequence *seq = seq_item->link;
- /* It's possible that effect strip would be placed to the same
- * 'machine' as its inputs. We don't want to clear such strips
- * from the stack. */
- if (seq_arr[seq->machine] && seq_arr[seq->machine]->type & SEQ_TYPE_EFFECT) {
- continue;
- }
- /* If we're shown a specified channel, then we want to see the strips
- * which belongs to this machine. */
- if (chanshown != 0 && chanshown <= seq->machine) {
+static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (seq->machine <= channel) {
continue;
}
- seq_arr[seq->machine] = NULL;
+ SEQ_collection_remove_strip(seq, collection);
}
-
- return totseq;
}
-/**
- * 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)
+/* Remove strips we don't want to render from collection. */
+static void collection_filter_rendered_strips(SeqCollection *collection)
{
- Sequence *seq_arr[MAXSEQ + 1];
- return evaluate_seq_frame_gen(seq_arr, seqbase, timeline_frame, 0);
+ Sequence *seq;
+
+ /* Remove sound strips and muted strips from collection, because these are not rendered.
+ * Function must_render_strip() don't have to check for these strips anymore. */
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) {
+ SEQ_collection_remove_strip(seq, collection);
+ }
+ }
+
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (must_render_strip(seq, collection)) {
+ continue;
+ }
+ SEQ_collection_remove_strip(seq, collection);
+ }
}
-static bool video_seq_is_rendered(Sequence *seq)
+static int seq_channel_cmp_fn(const void *a, const void *b)
{
- return (seq && !(seq->flag & SEQ_MUTE) && seq->type != SEQ_TYPE_SOUND_RAM);
+ return (*(Sequence **)a)->machine - (*(Sequence **)b)->machine;
}
-int seq_get_shown_sequences(ListBase *seqbasep,
- int timeline_frame,
- int chanshown,
- Sequence **seq_arr_out)
+int seq_get_shown_sequences(ListBase *seqbase,
+ const int timeline_frame,
+ const int chanshown,
+ Sequence **r_seq_arr)
{
- Sequence *seq_arr[MAXSEQ + 1];
- int b = chanshown;
- int cnt = 0;
+ SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame);
- if (b > MAXSEQ) {
- return 0;
+ if (chanshown != 0) {
+ collection_filter_channel_up_to_incl(collection, chanshown);
}
+ collection_filter_rendered_strips(collection);
- if (evaluate_seq_frame_gen(seq_arr, seqbasep, timeline_frame, chanshown)) {
- if (b == 0) {
- b = MAXSEQ;
- }
- for (; b > 0; b--) {
- if (video_seq_is_rendered(seq_arr[b])) {
- break;
- }
- }
- }
-
- chanshown = b;
+ const int strip_count = BLI_gset_len(collection->set);
- for (; b > 0; b--) {
- if (video_seq_is_rendered(seq_arr[b])) {
- if (seq_arr[b]->blend_mode == SEQ_BLEND_REPLACE) {
- break;
- }
- }
+ if (strip_count > MAXSEQ) {
+ BLI_assert(!"Too many strips, this shouldn't happen");
+ return 0;
}
- for (; b <= chanshown && b >= 0; b++) {
- if (video_seq_is_rendered(seq_arr[b])) {
- seq_arr_out[cnt++] = seq_arr[b];
- }
+ /* Copy collection elements into array. */
+ memset(r_seq_arr, 0, sizeof(Sequence *) * (MAXSEQ + 1));
+ Sequence *seq;
+ int index = 0;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ r_seq_arr[index] = seq;
+ index++;
}
+ SEQ_collection_free(collection);
- return cnt;
+ /* Sort array by channel. */
+ qsort(r_seq_arr, strip_count, sizeof(Sequence *), seq_channel_cmp_fn);
+
+ return strip_count;
}
/** \} */
@@ -456,84 +471,86 @@ static bool seq_input_have_to_preprocess(const SeqRenderData *context,
return false;
}
-typedef struct ImageTransformThreadInitData {
- ImBuf *ibuf_source;
- ImBuf *ibuf_out;
- StripTransform *transform;
- float scale_to_fit;
- float image_scale_factor;
- float preview_scale_factor;
- bool for_render;
-} ImageTransformThreadInitData;
-
-typedef struct ImageTransformThreadData {
- ImBuf *ibuf_source;
- ImBuf *ibuf_out;
- StripTransform *transform;
- float scale_to_fit;
- /* image_scale_factor is used to scale proxies to correct preview size. */
- float image_scale_factor;
- /* Preview scale factor is needed to correct translation to match preview size. */
- float preview_scale_factor;
- bool for_render;
- int start_line;
- int tot_line;
-} ImageTransformThreadData;
-
-static void sequencer_image_transform_init(void *handle_v,
- int start_line,
- int tot_line,
- void *init_data_v)
+/**
+ * Effect, mask and scene in strip input strips are rendered in preview resolution.
+ * They are already down-scaled. #input_preprocess() does not expect this to happen.
+ * Other strip types are rendered with original media resolution, unless proxies are
+ * enabled for them. With proxies `is_proxy_image` will be set correctly to true.
+ */
+static bool seq_need_scale_to_render_size(const Sequence *seq, bool is_proxy_image)
{
- ImageTransformThreadData *handle = (ImageTransformThreadData *)handle_v;
- const ImageTransformThreadInitData *init_data = (ImageTransformThreadInitData *)init_data_v;
-
- handle->ibuf_source = init_data->ibuf_source;
- handle->ibuf_out = init_data->ibuf_out;
- handle->transform = init_data->transform;
- handle->image_scale_factor = init_data->image_scale_factor;
- handle->preview_scale_factor = init_data->preview_scale_factor;
- handle->for_render = init_data->for_render;
-
- handle->start_line = start_line;
- handle->tot_line = tot_line;
+ if (is_proxy_image) {
+ return true;
+ }
+ if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->type == SEQ_TYPE_MASK ||
+ seq->type == SEQ_TYPE_META ||
+ (seq->type == SEQ_TYPE_SCENE && ((seq->flag & SEQ_SCENE_STRIPS) != 0))) {
+ return true;
+ }
+ return false;
}
-static void *sequencer_image_transform_do_thread(void *data_v)
+static void sequencer_image_crop_transform_matrix(const Sequence *seq,
+ const ImBuf *in,
+ const ImBuf *out,
+ const float image_scale_factor,
+ const float preview_scale_factor,
+ float r_transform_matrix[3][3])
{
- const ImageTransformThreadData *data = (ImageTransformThreadData *)data_v;
- const StripTransform *transform = data->transform;
- const float scale_x = transform->scale_x * data->image_scale_factor;
- const float scale_y = transform->scale_y * data->image_scale_factor;
- const float image_center_offs_x = (data->ibuf_out->x - data->ibuf_source->x) / 2;
- const float image_center_offs_y = (data->ibuf_out->y - data->ibuf_source->y) / 2;
- const float translate_x = transform->xofs * data->preview_scale_factor + image_center_offs_x;
- const float translate_y = transform->yofs * data->preview_scale_factor + image_center_offs_y;
- const float pivot[2] = {data->ibuf_source->x / 2, data->ibuf_source->y / 2};
- float transform_matrix[3][3];
- loc_rot_size_to_mat3(transform_matrix,
+ const StripTransform *transform = seq->strip->transform;
+ const float scale_x = transform->scale_x * image_scale_factor;
+ const float scale_y = transform->scale_y * image_scale_factor;
+ const float image_center_offs_x = (out->x - in->x) / 2;
+ const float image_center_offs_y = (out->y - in->y) / 2;
+ const float translate_x = transform->xofs * preview_scale_factor + image_center_offs_x;
+ const float translate_y = transform->yofs * preview_scale_factor + image_center_offs_y;
+ const float pivot[2] = {in->x / 2, in->y / 2};
+ loc_rot_size_to_mat3(r_transform_matrix,
(const float[]){translate_x, translate_y},
transform->rotation,
(const float[]){scale_x, scale_y});
- invert_m3(transform_matrix);
- transform_pivot_set_m3(transform_matrix, pivot);
+ transform_pivot_set_m3(r_transform_matrix, pivot);
+ invert_m3(r_transform_matrix);
+}
- const int width = data->ibuf_out->x;
- for (int yi = data->start_line; yi < data->start_line + data->tot_line; yi++) {
- for (int xi = 0; xi < width; xi++) {
- float uv[2] = {xi, yi};
- mul_v2_m3v2(uv, transform_matrix, uv);
+static void sequencer_image_crop_init(const Sequence *seq,
+ const ImBuf *in,
+ float crop_scale_factor,
+ rctf *r_crop)
+{
+ const StripCrop *c = seq->strip->crop;
+ const int left = c->left * crop_scale_factor;
+ const int right = c->right * crop_scale_factor;
+ const int top = c->top * crop_scale_factor;
+ const int bottom = c->bottom * crop_scale_factor;
- if (data->for_render) {
- bilinear_interpolation(data->ibuf_source, data->ibuf_out, uv[0], uv[1], xi, yi);
- }
- else {
- nearest_interpolation(data->ibuf_source, data->ibuf_out, uv[0], uv[1], xi, yi);
- }
- }
- }
+ BLI_rctf_init(r_crop, left, in->x - right, bottom, in->y - top);
+}
- return NULL;
+static void sequencer_preprocess_transform_crop(
+ ImBuf *in, ImBuf *out, const SeqRenderData *context, Sequence *seq, const bool is_proxy_image)
+{
+ const Scene *scene = context->scene;
+ const float preview_scale_factor = context->preview_render_size == SEQ_RENDER_SIZE_SCENE ?
+ (float)scene->r.size / 100 :
+ SEQ_rendersize_to_scale_factor(
+ context->preview_render_size);
+ const bool do_scale_to_render_size = seq_need_scale_to_render_size(seq, is_proxy_image);
+ const float image_scale_factor = do_scale_to_render_size ? 1.0f : preview_scale_factor;
+
+ float transform_matrix[3][3];
+ sequencer_image_crop_transform_matrix(
+ seq, in, out, image_scale_factor, preview_scale_factor, transform_matrix);
+
+ /* Proxy image is smaller, so crop values must be corrected by proxy scale factor.
+ * Proxy scale factor always matches preview_scale_factor. */
+ rctf source_crop;
+ const float crop_scale_factor = do_scale_to_render_size ? preview_scale_factor : 1.0f;
+ sequencer_image_crop_init(seq, in, crop_scale_factor, &source_crop);
+
+ const eIMBInterpolationFilterMode filter = context->for_render ? IMB_FILTER_BILINEAR :
+ IMB_FILTER_NEAREST;
+ IMB_transform(in, out, transform_matrix, &source_crop, filter);
}
static void multibuf(ImBuf *ibuf, const float fmul)
@@ -571,25 +588,6 @@ static void multibuf(ImBuf *ibuf, const float fmul)
}
}
-/**
- * Effect, mask and scene in strip input strips are rendered in preview resolution.
- * They are already down-scaled. #input_preprocess() does not expect this to happen.
- * Other strip types are rendered with original media resolution, unless proxies are
- * enabled for them. With proxies `is_proxy_image` will be set correctly to true.
- */
-static bool seq_need_scale_to_render_size(const Sequence *seq, bool is_proxy_image)
-{
- if (is_proxy_image) {
- return true;
- }
- if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->type == SEQ_TYPE_MASK ||
- seq->type == SEQ_TYPE_META ||
- (seq->type == SEQ_TYPE_SCENE && ((seq->flag & SEQ_SCENE_STRIPS) != 0))) {
- return true;
- }
- return false;
-}
-
static ImBuf *input_preprocess(const SeqRenderData *context,
Sequence *seq,
float timeline_frame,
@@ -608,67 +606,14 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
IMB_filtery(preprocessed_ibuf);
}
- /* Get scale factor if preview resolution doesn't match project resolution. */
- float preview_scale_factor;
- if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) {
- preview_scale_factor = (float)scene->r.size / 100;
- }
- else {
- preview_scale_factor = SEQ_rendersize_to_scale_factor(context->preview_render_size);
- }
-
- if (sequencer_use_crop(seq)) {
- /* Change original image pointer to avoid another duplication in SEQ_USE_TRANSFORM. */
- preprocessed_ibuf = IMB_makeSingleUser(ibuf);
- ibuf = preprocessed_ibuf;
-
- const int width = ibuf->x;
- const int height = ibuf->y;
- const StripCrop *c = seq->strip->crop;
-
- /* Proxy image is smaller, so crop values must be corrected by proxy scale factor.
- * Proxy scale factor always matches preview_scale_factor. */
- const float crop_scale_factor = seq_need_scale_to_render_size(seq, is_proxy_image) ?
- preview_scale_factor :
- 1.0f;
- const int left = c->left * crop_scale_factor;
- const int right = c->right * crop_scale_factor;
- const int top = c->top * crop_scale_factor;
- const int bottom = c->bottom * crop_scale_factor;
- const float col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
- /* Left. */
- IMB_rectfill_area_replace(preprocessed_ibuf, col, 0, 0, left, height);
- /* Bottom. */
- IMB_rectfill_area_replace(preprocessed_ibuf, col, left, 0, width, bottom);
- /* Right. */
- IMB_rectfill_area_replace(preprocessed_ibuf, col, width - right, bottom, width, height);
- /* Top. */
- IMB_rectfill_area_replace(preprocessed_ibuf, col, left, height - top, width - right, height);
- }
-
- if (sequencer_use_transform(seq) || context->rectx != ibuf->x || context->recty != ibuf->y) {
+ if (sequencer_use_crop(seq) || sequencer_use_transform(seq) || context->rectx != ibuf->x ||
+ context->recty != ibuf->y) {
const int x = context->rectx;
const int y = context->recty;
preprocessed_ibuf = IMB_allocImBuf(x, y, 32, ibuf->rect_float ? IB_rectfloat : IB_rect);
- ImageTransformThreadInitData init_data = {NULL};
- init_data.ibuf_source = ibuf;
- init_data.ibuf_out = preprocessed_ibuf;
- init_data.transform = seq->strip->transform;
- if (seq_need_scale_to_render_size(seq, is_proxy_image)) {
- init_data.image_scale_factor = 1.0f;
- }
- else {
- init_data.image_scale_factor = preview_scale_factor;
- }
- init_data.preview_scale_factor = preview_scale_factor;
- init_data.for_render = context->for_render;
- IMB_processor_apply_threaded(context->recty,
- sizeof(ImageTransformThreadData),
- &init_data,
- sequencer_image_transform_init,
- sequencer_image_transform_do_thread);
+ sequencer_preprocess_transform_crop(ibuf, preprocessed_ibuf, context, seq, is_proxy_image);
+
seq_imbuf_assign_spaces(scene, preprocessed_ibuf);
IMB_metadata_copy(preprocessed_ibuf, ibuf);
IMB_freeImBuf(ibuf);
@@ -1117,8 +1062,6 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
ImBuf *ibuf = NULL;
IMB_Proxy_Size psize = SEQ_rendersize_to_proxysize(context->preview_render_size);
- IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
-
if (SEQ_can_use_proxy(context, seq, psize)) {
/* Try to get a proxy image.
* Movie proxies are handled by ImBuf module with exception of `custom file` setting. */
@@ -1230,6 +1173,12 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
}
if (*r_is_proxy_image == false) {
+ if (sanim && sanim->anim) {
+ short fps_denom;
+ float fps_num;
+ IMB_anim_get_fps(sanim->anim, &fps_denom, &fps_num, true);
+ seq->strip->stripdata->orig_fps = fps_denom / fps_num;
+ }
seq->strip->stripdata->orig_width = ibuf->x;
seq->strip->stripdata->orig_height = ibuf->y;
}
@@ -1501,7 +1450,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
goto finally;
}
- if (seq->flag & SEQ_SCENE_NO_GPENCIL) {
+ if (seq->flag & SEQ_SCENE_NO_ANNOTATION) {
use_gpencil = false;
}
@@ -1537,13 +1486,14 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
/* opengl offscreen render */
depsgraph = BKE_scene_ensure_depsgraph(context->bmain, scene, view_layer);
BKE_scene_graph_update_for_newframe(depsgraph);
+ Object *camera_eval = DEG_get_evaluated_object(depsgraph, camera);
ibuf = sequencer_view3d_fn(
/* set for OpenGL render (NULL when scrubbing) */
depsgraph,
scene,
&context->scene->display.shading,
context->scene->r.seq_prev_type,
- camera,
+ camera_eval,
width,
height,
IB_rect,
@@ -1966,7 +1916,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
/**
* \return The image buffer or NULL.
*
- * \note The returned #ImBuf is has it's reference increased, free after usage!
+ * \note The returned #ImBuf has its reference increased, free after usage!
*/
ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame, int chanshown)
{
diff --git a/source/blender/sequencer/intern/render.h b/source/blender/sequencer/intern/render.h
index 1147516b8ec..a0cdf24d84b 100644
--- a/source/blender/sequencer/intern/render.h
+++ b/source/blender/sequencer/intern/render.h
@@ -60,10 +60,10 @@ struct ImBuf *seq_render_effect_execute_threaded(struct SeqEffectHandle *sh,
struct ImBuf *ibuf2,
struct ImBuf *ibuf3);
void seq_imbuf_to_sequencer_space(struct Scene *scene, struct ImBuf *ibuf, bool make_float);
-int seq_get_shown_sequences(struct ListBase *seqbasep,
+int seq_get_shown_sequences(struct ListBase *seqbase,
int timeline_frame,
int chanshown,
- struct Sequence **seq_arr_out);
+ struct Sequence **r_seq_arr);
struct ImBuf *seq_render_strip(const struct SeqRenderData *context,
struct SeqRenderState *state,
struct Sequence *seq,
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index cc11796496c..d0bc41062a1 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -313,6 +313,17 @@ SequencerToolSettings *SEQ_tool_settings_init(void)
return tool_settings;
}
+SequencerToolSettings *SEQ_tool_settings_ensure(Scene *scene)
+{
+ SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ if (tool_settings == NULL) {
+ scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
+ tool_settings = scene->toolsettings->sequencer_tool_settings;
+ }
+
+ return tool_settings;
+}
+
void SEQ_tool_settings_free(SequencerToolSettings *tool_settings)
{
MEM_freeN(tool_settings);
@@ -320,13 +331,13 @@ void SEQ_tool_settings_free(SequencerToolSettings *tool_settings)
eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene)
{
- const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
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;
+ SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
tool_settings->fit_method = fit_method;
}
@@ -448,8 +459,8 @@ static Sequence *seq_dupli(const Scene *scene_src,
seqn->strip->stripdata = NULL;
BLI_listbase_clear(&seqn->seqbase);
- /* WATCH OUT!!! - This metastrip is not recursively duplicated here - do this after!!! */
- /* - seq_dupli_recursive(&seq->seqbase, &seqn->seqbase);*/
+ /* WARNING: This meta-strip is not recursively duplicated here - do this after! */
+ // seq_dupli_recursive(&seq->seqbase, &seqn->seqbase);
}
else if (seq->type == SEQ_TYPE_SCENE) {
seqn->strip->stripdata = NULL;
@@ -505,10 +516,6 @@ static Sequence *seq_dupli(const Scene *scene_src,
if (dupe_flag & SEQ_DUPE_UNIQUE_NAME) {
SEQ_sequence_base_unique_name_recursive(&scene_dst->ed->seqbase, seqn);
}
-
- if (dupe_flag & SEQ_DUPE_ANIM) {
- SEQ_dupe_animdata(scene_dst, seq->name + 2, seqn->name + 2);
- }
}
return seqn;
@@ -554,30 +561,21 @@ void SEQ_sequence_base_dupli_recursive(const Scene *scene_src,
{
Sequence *seq;
Sequence *seqn = NULL;
- Sequence *last_seq = SEQ_select_active_get((Scene *)scene_src);
- /* always include meta's strips */
- int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL;
for (seq = seqbase->first; seq; seq = seq->next) {
seq->tmp = NULL;
if ((seq->flag & SELECT) || (dupe_flag & SEQ_DUPE_ALL)) {
seqn = seq_dupli(scene_src, scene_dst, nseqbase, seq, dupe_flag, flag);
- if (seqn) { /*should never fail */
- if (dupe_flag & SEQ_DUPE_CONTEXT) {
- seq->flag &= ~SEQ_ALLSEL;
- seqn->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK);
- }
- if (seq->type == SEQ_TYPE_META) {
- SEQ_sequence_base_dupli_recursive(
- scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag);
- }
+ if (seqn == NULL) {
+ continue; /* Should never fail. */
+ }
- if (dupe_flag & SEQ_DUPE_CONTEXT) {
- if (seq == last_seq) {
- SEQ_select_active_set(scene_dst, seqn);
- }
- }
+ if (seq->type == SEQ_TYPE_META) {
+ /* Always include meta all strip children. */
+ int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL;
+ SEQ_sequence_base_dupli_recursive(
+ scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag);
}
}
}
diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c
index 33dd74cb527..888220b7111 100644
--- a/source/blender/sequencer/intern/strip_add.c
+++ b/source/blender/sequencer/intern/strip_add.c
@@ -50,6 +50,7 @@
#include "DEG_depsgraph_query.h"
+#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_metadata.h"
@@ -99,7 +100,7 @@ static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *se
{
SEQ_sequence_base_unique_name_recursive(seqbase, seq);
SEQ_time_update_sequence_bounds(scene, seq);
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
@@ -128,6 +129,24 @@ static void seq_add_set_name(Sequence *seq, SeqLoadData *load_data)
}
}
+static void seq_add_set_view_transform(Scene *scene, Sequence *seq, SeqLoadData *load_data)
+{
+ const char *strip_colorspace = seq->strip->colorspace_settings.name;
+
+ if (load_data->flags & SEQ_LOAD_SET_VIEW_TRANSFORM) {
+ const char *role_colorspace_byte;
+ role_colorspace_byte = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE);
+
+ if (STREQ(strip_colorspace, role_colorspace_byte)) {
+ struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(
+ scene->display_settings.display_device);
+ const char *default_view_transform =
+ IMB_colormanagement_display_get_default_view_transform_name(display);
+ STRNCPY(scene->view_settings.view_transform, default_view_transform);
+ }
+ }
+}
+
/**
* Add scene strip.
*
@@ -324,7 +343,7 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
/* Set initial scale based on load_data->fit_method. */
char file_path[FILE_MAX];
- BLI_join_dirfile(file_path, sizeof(file_path), load_data->path, load_data->name);
+ BLI_strncpy(file_path, load_data->path, sizeof(file_path));
BLI_path_abs(file_path, BKE_main_blendfile_path(bmain));
ImBuf *ibuf = IMB_loadiffname(file_path, IB_rect, seq->strip->colorspace_settings.name);
if (ibuf != NULL) {
@@ -344,6 +363,7 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
/* Set Last active directory. */
BLI_strncpy(scene->ed->act_imagedir, seq->strip->dir, sizeof(scene->ed->act_imagedir));
+ seq_add_set_view_transform(scene, seq, load_data);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
@@ -389,7 +409,7 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
Strip *strip = seq->strip;
/* We only need 1 element to store the filename. */
- StripElem *se = strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
+ StripElem *se = strip->stripdata = MEM_callocN(sizeof(StripElem), "stripelem");
BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
if (seq != NULL && seq->sound != NULL) {
@@ -528,15 +548,24 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */
+ float video_fps = 0.0f;
+
if (anim_arr[0] != NULL) {
- seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]);
seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN);
IMB_anim_load_metadata(anim_arr[0]);
+ short fps_denom;
+ float fps_num;
+
+ IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true);
+
+ video_fps = fps_denom / fps_num;
+
/* Adjust scene's frame rate settings to match. */
if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) {
- IMB_anim_get_fps(anim_arr[0], &scene->r.frs_sec, &scene->r.frs_sec_base, true);
+ scene->r.frs_sec = fps_denom;
+ scene->r.frs_sec_base = fps_num;
}
/* Set initial scale based on load_data->fit_method. */
@@ -557,8 +586,10 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
strip->stripdata->orig_width = orig_width;
strip->stripdata->orig_height = orig_height;
+ strip->stripdata->orig_fps = video_fps;
BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
+ seq_add_set_view_transform(scene, seq, load_data);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
@@ -670,8 +701,6 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo
seq->len = IMB_anim_get_duration(
sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN);
- seq->anim_preseek = IMB_anim_get_preseek(sanim->anim);
-
seq->len -= seq->anim_startofs;
seq->len -= seq->anim_endofs;
if (seq->len < 0) {
diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c
index 4a27fb3a087..4de6ec3583c 100644
--- a/source/blender/sequencer/intern/strip_edit.c
+++ b/source/blender/sequencer/intern/strip_edit.c
@@ -39,10 +39,12 @@
#include "BKE_sound.h"
#include "strip_time.h"
+#include "utils.h"
#include "SEQ_add.h"
#include "SEQ_edit.h"
#include "SEQ_effects.h"
+#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@@ -149,7 +151,7 @@ void SEQ_edit_update_muting(Editing *ed)
static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq)
{
LISTBASE_FOREACH (Sequence *, user_seq, seqbase) {
- /* Look in metas for usage of seq. */
+ /* Look in meta-strips for usage of seq. */
if (user_seq->type == SEQ_TYPE_META) {
sequencer_flag_users_for_removal(scene, &user_seq->seqbase, seq);
}
@@ -251,22 +253,26 @@ bool SEQ_edit_move_strip_to_meta(Scene *scene,
return false;
}
- /* Remove users of src_seq. Ideally these could be moved into meta as well, but this would be
- * best to do with generalized iterator as described in D10337. */
- sequencer_flag_users_for_removal(scene, seqbase, src_seq);
- SEQ_edit_remove_flagged_sequences(scene, seqbase);
+ SeqCollection *collection = SEQ_collection_create();
+ SEQ_collection_append_strip(src_seq, collection);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
- /* Move to meta. */
- BLI_remlink(seqbase, src_seq);
- BLI_addtail(&dst_seqm->seqbase, src_seq);
- SEQ_relations_invalidate_cache_preprocessed(scene, src_seq);
-
- /* Update meta. */
- SEQ_time_update_sequence(scene, dst_seqm);
- if (SEQ_transform_test_overlap(&dst_seqm->seqbase, src_seq)) {
- SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, src_seq, scene);
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ /* Move to meta. */
+ BLI_remlink(seqbase, seq);
+ BLI_addtail(&dst_seqm->seqbase, seq);
+ SEQ_relations_invalidate_cache_preprocessed(scene, seq);
+
+ /* Update meta. */
+ SEQ_time_update_sequence(scene, dst_seqm);
+ if (SEQ_transform_test_overlap(&dst_seqm->seqbase, seq)) {
+ SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, seq, scene);
+ }
}
+ SEQ_collection_free(collection);
+
return true;
}
@@ -343,6 +349,29 @@ static void seq_split_set_left_offset(Sequence *seq, int timeline_frame)
SEQ_transform_set_left_handle_frame(seq, timeline_frame);
}
+static void seq_edit_split_handle_strip_offsets(Main *bmain,
+ Scene *scene,
+ Sequence *left_seq,
+ Sequence *right_seq,
+ const int timeline_frame,
+ const eSeqSplitMethod method)
+{
+ switch (method) {
+ case SEQ_SPLIT_SOFT:
+ seq_split_set_left_offset(right_seq, timeline_frame);
+ seq_split_set_right_offset(left_seq, timeline_frame);
+ break;
+ case SEQ_SPLIT_HARD:
+ seq_split_set_right_hold_offset(left_seq, timeline_frame);
+ seq_split_set_left_hold_offset(right_seq, timeline_frame);
+ SEQ_add_reload_new_file(bmain, scene, left_seq, false);
+ SEQ_add_reload_new_file(bmain, scene, right_seq, false);
+ break;
+ }
+ SEQ_time_update_sequence(scene, left_seq);
+ SEQ_time_update_sequence(scene, right_seq);
+}
+
/**
* Split Sequence at timeline_frame in two.
*
@@ -365,33 +394,44 @@ Sequence *SEQ_edit_strip_split(Main *bmain,
return NULL;
}
- if (method == SEQ_SPLIT_HARD) {
- /* Precaution, needed because the length saved on-disk may not match the length saved in the
- * blend file, or our code may have minor differences reading file length between versions.
- * This causes hard-split to fail, see: T47862. */
- SEQ_add_reload_new_file(bmain, scene, seq, true);
- SEQ_time_update_sequence(scene, seq);
+ SeqCollection *collection = SEQ_collection_create();
+ SEQ_collection_append_strip(seq, collection);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
+
+ /* Move strips in collection from seqbase to new ListBase. */
+ ListBase left_strips = {NULL, NULL};
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ BLI_remlink(seqbase, seq);
+ BLI_addtail(&left_strips, seq);
}
- Sequence *left_seq = seq;
- Sequence *right_seq = SEQ_sequence_dupli_recursive(
- scene, scene, seqbase, seq, SEQ_DUPE_UNIQUE_NAME | SEQ_DUPE_ANIM);
+ /* Sort list, so that no strip can depend on next strip in list.
+ * This is important for SEQ_time_update_sequence functionality. */
+ SEQ_sort(&left_strips);
+
+ /* Duplicate ListBase. */
+ ListBase right_strips = {NULL, NULL};
+ SEQ_sequence_base_dupli_recursive(scene, scene, &right_strips, &left_strips, SEQ_DUPE_ALL, 0);
+
+ /* Split strips. */
+ Sequence *left_seq = left_strips.first;
+ Sequence *right_seq = right_strips.first;
+ Sequence *return_seq = right_strips.first;
+ while (left_seq && right_seq) {
+ seq_edit_split_handle_strip_offsets(bmain, scene, left_seq, right_seq, timeline_frame, method);
+ left_seq = left_seq->next;
+ right_seq = right_seq->next;
+ }
- switch (method) {
- case SEQ_SPLIT_SOFT:
- seq_split_set_left_offset(right_seq, timeline_frame);
- seq_split_set_right_offset(left_seq, timeline_frame);
- break;
- case SEQ_SPLIT_HARD:
- seq_split_set_right_hold_offset(left_seq, timeline_frame);
- seq_split_set_left_hold_offset(right_seq, timeline_frame);
- SEQ_add_reload_new_file(bmain, scene, left_seq, false);
- SEQ_add_reload_new_file(bmain, scene, right_seq, false);
- break;
+ seq = right_strips.first;
+ BLI_movelisttolist(seqbase, &left_strips);
+ BLI_movelisttolist(seqbase, &right_strips);
+
+ for (; seq; seq = seq->next) {
+ SEQ_ensure_unique_name(seq, scene);
}
- SEQ_time_update_sequence(scene, left_seq);
- SEQ_time_update_sequence(scene, right_seq);
- return right_seq;
+
+ return return_seq;
}
/**
diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c
index 1a2ff08bd08..7c5a3f031db 100644
--- a/source/blender/sequencer/intern/strip_relations.c
+++ b/source/blender/sequencer/intern/strip_relations.c
@@ -114,7 +114,6 @@ static void sequence_invalidate_cache(Scene *scene,
Editing *ed = scene->ed;
if (invalidate_self) {
- SEQ_relations_sequence_free_anim(seq);
seq_cache_cleanup_sequence(scene, seq, seq, invalidate_types, false);
}
@@ -260,7 +259,7 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
SEQ_prefetch_stop(scene);
for (seq = seqbase->first; seq; seq = seq->next) {
- if (for_render && CFRA >= seq->startdisp && CFRA <= seq->enddisp) {
+ if (for_render && SEQ_time_strip_intersects_frame(seq, CFRA)) {
continue;
}
@@ -359,7 +358,7 @@ void SEQ_relations_update_changed_seq_and_deps(Scene *scene,
static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame)
{
for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) {
- if (seq->enddisp < timeline_frame || seq->startdisp > timeline_frame) {
+ if (!SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
SEQ_relations_sequence_free_anim(seq);
}
if (seq->type == SEQ_TYPE_META) {
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index 21dc9aa2cdd..b8b6f62c7aa 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -36,9 +36,11 @@
#include "IMB_imbuf.h"
+#include "SEQ_iterator.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
+#include "SEQ_transform.h"
#include "strip_time.h"
#include "utils.h"
@@ -161,11 +163,41 @@ void SEQ_time_update_sequence_bounds(Scene *scene, Sequence *seq)
}
}
+static void seq_time_update_meta_strip(Scene *scene, Sequence *seq_meta)
+{
+ if (BLI_listbase_is_empty(&seq_meta->seqbase)) {
+ return;
+ }
+
+ int min = MAXFRAME * 2;
+ int max = -MAXFRAME * 2;
+ LISTBASE_FOREACH (Sequence *, seq, &seq_meta->seqbase) {
+ min = min_ii(seq->startdisp, min);
+ max = max_ii(seq->enddisp, max);
+ }
+
+ seq_meta->start = min + seq_meta->anim_startofs;
+ seq_meta->len = max - min;
+ seq_meta->len -= seq_meta->anim_startofs;
+ seq_meta->len -= seq_meta->anim_endofs;
+
+ seq_update_sound_bounds_recursive(scene, seq_meta);
+}
+
+static void seq_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta)
+{
+ seq_time_update_meta_strip(scene, seq_meta);
+
+ /* Prevent meta-strip to move in timeline. */
+ SEQ_transform_set_left_handle_frame(seq_meta, seq_meta->startdisp);
+ SEQ_transform_set_right_handle_frame(seq_meta, seq_meta->enddisp);
+}
+
void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
{
Sequence *seqm;
- /* check all metas recursively */
+ /* Check all meta-strips recursively. */
seqm = seq->seqbase.first;
while (seqm) {
if (seqm->seqbase.first) {
@@ -211,6 +243,16 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
}
}
else {
+ if (seq->type == SEQ_TYPE_META) {
+ seq_time_update_meta_strip(scene, seq);
+ }
+
+ Editing *ed = SEQ_editing_get(scene, false);
+ MetaStack *ms = SEQ_meta_stack_active_get(ed);
+ if (ms != NULL) {
+ seq_time_update_meta_strip_range(scene, ms->parseq);
+ }
+
SEQ_time_update_sequence_bounds(scene, seq);
}
}
@@ -363,6 +405,17 @@ void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *re
}
}
+static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_frame)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, all_strips) {
+ if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* Find first gap between strips after initial_frame and describe it by filling data of r_gap_info
*
@@ -384,10 +437,12 @@ void seq_time_gap_info_get(const Scene *scene,
int timeline_frame = initial_frame;
r_gap_info->gap_exists = false;
- if (SEQ_render_evaluate_frame(seqbase, initial_frame) == 0) {
+ SeqCollection *collection = SEQ_query_all_strips(seqbase);
+
+ if (!strip_exists_at_frame(collection, initial_frame)) {
/* Search backward for gap_start_frame. */
for (; timeline_frame >= sfra; timeline_frame--) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
+ if (strip_exists_at_frame(collection, timeline_frame)) {
break;
}
}
@@ -397,7 +452,7 @@ void seq_time_gap_info_get(const Scene *scene,
else {
/* Search forward for gap_start_frame. */
for (; timeline_frame <= efra; timeline_frame++) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) == 0) {
+ if (!strip_exists_at_frame(collection, timeline_frame)) {
r_gap_info->gap_start_frame = timeline_frame;
break;
}
@@ -405,7 +460,7 @@ void seq_time_gap_info_get(const Scene *scene,
}
/* Search forward for gap_end_frame. */
for (; timeline_frame <= efra; timeline_frame++) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
+ if (strip_exists_at_frame(collection, timeline_frame)) {
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;
@@ -413,3 +468,17 @@ void seq_time_gap_info_get(const Scene *scene,
}
}
}
+
+/**
+ * Test if strip intersects with timeline frame.
+ * Note: This checks if strip would be rendered at this frame. For rendering it is assumed, that
+ * timeline frame has width of 1 frame and therefore ends at timeline_frame + 1
+ *
+ * \param seq: Sequence to be checked
+ * \param timeline_frame: absolute frame position
+ * \return true if strip intersects with timeline frame.
+ */
+bool SEQ_time_strip_intersects_frame(const Sequence *seq, const int timeline_frame)
+{
+ return (seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame);
+}
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index 3a0ba36b795..a02d8a1428e 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -48,24 +48,12 @@ static int seq_tx_get_end(Sequence *seq)
return seq->start + seq->len;
}
-int SEQ_transform_get_left_handle_frame(Sequence *seq, bool metaclip)
+int SEQ_transform_get_left_handle_frame(Sequence *seq)
{
- if (metaclip && seq->tmp) {
- /* return the range clipped by the parents range */
- return max_ii(SEQ_transform_get_left_handle_frame(seq, false),
- SEQ_transform_get_left_handle_frame((Sequence *)seq->tmp, true));
- }
-
return (seq->start - seq->startstill) + seq->startofs;
}
-int SEQ_transform_get_right_handle_frame(Sequence *seq, bool metaclip)
+int SEQ_transform_get_right_handle_frame(Sequence *seq)
{
- if (metaclip && seq->tmp) {
- /* return the range clipped by the parents range */
- return min_ii(SEQ_transform_get_right_handle_frame(seq, false),
- SEQ_transform_get_right_handle_frame((Sequence *)seq->tmp, true));
- }
-
return ((seq->start + seq->len) + seq->endstill) - seq->endofs;
}
@@ -151,14 +139,12 @@ bool SEQ_transform_seqbase_isolated_sel_check(ListBase *seqbase)
void SEQ_transform_handle_xlimits(Sequence *seq, int leftflag, int rightflag)
{
if (leftflag) {
- if (SEQ_transform_get_left_handle_frame(seq, false) >=
- SEQ_transform_get_right_handle_frame(seq, false)) {
- SEQ_transform_set_left_handle_frame(seq,
- SEQ_transform_get_right_handle_frame(seq, false) - 1);
+ if (SEQ_transform_get_left_handle_frame(seq) >= SEQ_transform_get_right_handle_frame(seq)) {
+ SEQ_transform_set_left_handle_frame(seq, SEQ_transform_get_right_handle_frame(seq) - 1);
}
if (SEQ_transform_single_image_check(seq) == 0) {
- if (SEQ_transform_get_left_handle_frame(seq, false) >= seq_tx_get_end(seq)) {
+ if (SEQ_transform_get_left_handle_frame(seq) >= seq_tx_get_end(seq)) {
SEQ_transform_set_left_handle_frame(seq, seq_tx_get_end(seq) - 1);
}
@@ -175,14 +161,12 @@ void SEQ_transform_handle_xlimits(Sequence *seq, int leftflag, int rightflag)
}
if (rightflag) {
- if (SEQ_transform_get_right_handle_frame(seq, false) <=
- SEQ_transform_get_left_handle_frame(seq, false)) {
- SEQ_transform_set_right_handle_frame(seq,
- SEQ_transform_get_left_handle_frame(seq, false) + 1);
+ if (SEQ_transform_get_right_handle_frame(seq) <= SEQ_transform_get_left_handle_frame(seq)) {
+ SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) + 1);
}
if (SEQ_transform_single_image_check(seq) == 0) {
- if (SEQ_transform_get_right_handle_frame(seq, false) <= seq_tx_get_start(seq)) {
+ if (SEQ_transform_get_right_handle_frame(seq) <= seq_tx_get_start(seq)) {
SEQ_transform_set_right_handle_frame(seq, seq_tx_get_start(seq) + 1);
}
}
@@ -204,14 +188,12 @@ void SEQ_transform_fix_single_image_seq_offsets(Sequence *seq)
/* make sure the image is always at the start since there is only one,
* adjusting its start should be ok */
- left = SEQ_transform_get_left_handle_frame(seq, false);
+ left = SEQ_transform_get_left_handle_frame(seq);
start = seq->start;
if (start != left) {
offset = left - start;
- SEQ_transform_set_left_handle_frame(seq,
- SEQ_transform_get_left_handle_frame(seq, false) - offset);
- SEQ_transform_set_right_handle_frame(
- seq, SEQ_transform_get_right_handle_frame(seq, false) - offset);
+ SEQ_transform_set_left_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) - offset);
+ SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_right_handle_frame(seq) - offset);
seq->start += offset;
}
}
diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c
index a15465eb3c0..9aeb2961751 100644
--- a/source/blender/sequencer/intern/utils.c
+++ b/source/blender/sequencer/intern/utils.c
@@ -33,10 +33,7 @@
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
-#include "BLI_listbase.h"
-#include "BLI_path_util.h"
-#include "BLI_string.h"
-#include "BLI_utildefines.h"
+#include "BLI_blenlib.h"
#include "BKE_image.h"
#include "BKE_main.h"
@@ -46,6 +43,7 @@
#include "SEQ_relations.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "SEQ_utils.h"
#include "IMB_imbuf.h"
@@ -55,21 +53,27 @@
#include "proxy.h"
#include "utils.h"
-void SEQ_sort(Scene *scene)
+/**
+ * Sort strips in provided seqbase. Effect strips are trailing the list and they are sorted by
+ * channel position as well.
+ * This is important for SEQ_time_update_sequence to work properly
+ *
+ * \param seqbase: ListBase with strips
+ */
+void SEQ_sort(ListBase *seqbase)
{
- /* all strips together per kind, and in order of y location ("machine") */
- ListBase seqbase, effbase;
- Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq, *seqt;
-
- if (ed == NULL) {
+ if (seqbase == NULL) {
return;
}
- BLI_listbase_clear(&seqbase);
+ /* all strips together per kind, and in order of y location ("machine") */
+ ListBase inputbase, effbase;
+ Sequence *seq, *seqt;
+
+ BLI_listbase_clear(&inputbase);
BLI_listbase_clear(&effbase);
- while ((seq = BLI_pophead(ed->seqbasep))) {
+ while ((seq = BLI_pophead(seqbase))) {
if (seq->type & SEQ_TYPE_EFFECT) {
seqt = effbase.first;
@@ -85,22 +89,22 @@ void SEQ_sort(Scene *scene)
}
}
else {
- seqt = seqbase.first;
+ seqt = inputbase.first;
while (seqt) {
if (seqt->machine >= seq->machine) {
- BLI_insertlinkbefore(&seqbase, seqt, seq);
+ BLI_insertlinkbefore(&inputbase, seqt, seq);
break;
}
seqt = seqt->next;
}
if (seqt == NULL) {
- BLI_addtail(&seqbase, seq);
+ BLI_addtail(&inputbase, seq);
}
}
}
- BLI_movelisttolist(&seqbase, &effbase);
- *(ed->seqbasep) = seqbase;
+ BLI_movelisttolist(seqbase, &inputbase);
+ BLI_movelisttolist(seqbase, &effbase);
}
typedef struct SeqUniqueInfo {
@@ -160,7 +164,7 @@ void SEQ_sequence_base_unique_name_recursive(ListBase *seqbasep, Sequence *seq)
while (sui.match) {
sui.match = 0;
seqbase_unique_name(seqbasep, &sui);
- SEQ_iterator_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui);
+ SEQ_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui);
}
BLI_strncpy(seq->name + 2, sui.name_dest, sizeof(seq->name) - 2);
@@ -403,7 +407,7 @@ const Sequence *SEQ_get_topmost_sequence(const Scene *scene, int frame)
}
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if (seq->flag & SEQ_MUTE || seq->startdisp > frame || seq->enddisp <= frame) {
+ if (seq->flag & SEQ_MUTE || !SEQ_time_strip_intersects_frame(seq, frame)) {
continue;
}
/* Only use strips that generate an image, not ones that combine
@@ -584,3 +588,53 @@ void SEQ_set_scale_to_fit(const Sequence *seq,
break;
}
}
+
+int SEQ_seqbase_recursive_apply(ListBase *seqbase,
+ int (*apply_fn)(Sequence *seq, void *),
+ void *arg)
+{
+ Sequence *iseq;
+ for (iseq = seqbase->first; iseq; iseq = iseq->next) {
+ if (SEQ_recursive_apply(iseq, apply_fn, arg) == -1) {
+ return -1; /* bail out */
+ }
+ }
+ return 1;
+}
+
+int SEQ_recursive_apply(Sequence *seq, int (*apply_fn)(Sequence *, void *), void *arg)
+{
+ int ret = apply_fn(seq, arg);
+
+ if (ret == -1) {
+ return -1; /* bail out */
+ }
+
+ if (ret && seq->seqbase.first) {
+ ret = SEQ_seqbase_recursive_apply(&seq->seqbase, apply_fn, arg);
+ }
+
+ return ret;
+}
+
+/**
+ * Ensure, that provided Sequence has unique name. If animation data exists for this Sequence, it
+ * will be duplicated and mapped onto new name
+ *
+ * \param seq: Sequence which name will be ensured to be unique
+ * \param scene: Scene in which name must be unique
+ */
+void SEQ_ensure_unique_name(Sequence *seq, Scene *scene)
+{
+ char name[SEQ_NAME_MAXSTR];
+
+ BLI_strncpy_utf8(name, seq->name + 2, sizeof(name));
+ SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
+ SEQ_dupe_animdata(scene, name, seq->name + 2);
+
+ if (seq->type == SEQ_TYPE_META) {
+ LISTBASE_FOREACH (Sequence *, seq_child, &seq->seqbase) {
+ SEQ_ensure_unique_name(seq_child, scene);
+ }
+ }
+}
diff --git a/source/blender/shader_fx/intern/FX_shader_glow.c b/source/blender/shader_fx/intern/FX_shader_glow.c
index 30eaa35a049..fc75771cd62 100644
--- a/source/blender/shader_fx/intern/FX_shader_glow.c
+++ b/source/blender/shader_fx/intern/FX_shader_glow.c
@@ -71,12 +71,11 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
- if (mode == eShaderFxGlowMode_Luminance) {
- uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE);
- }
- else {
+ uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE);
+ if (mode == eShaderFxGlowMode_Color) {
uiItemR(layout, ptr, "select_color", 0, NULL, ICON_NONE);
}
+
uiItemR(layout, ptr, "glow_color", 0, NULL, ICON_NONE);
uiItemS(layout);
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 0f26ec50816..183b22c9791 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -203,6 +203,7 @@ if(WITH_XR_OPENXR)
list(APPEND SRC
xr/intern/wm_xr.c
+ xr/intern/wm_xr_actions.c
xr/intern/wm_xr_draw.c
xr/intern/wm_xr_session.c
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 1a505b91ac5..3525502a6dc 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -71,6 +71,11 @@ struct wmTabletData;
struct wmNDOFMotionData;
#endif
+#ifdef WITH_XR_OPENXR
+struct wmXrActionState;
+struct wmXrPose;
+#endif
+
typedef struct wmGizmo wmGizmo;
typedef struct wmGizmoMap wmGizmoMap;
typedef struct wmGizmoMapType wmGizmoMapType;
@@ -185,6 +190,7 @@ struct wmWindow *WM_window_open(struct bContext *C,
int sizex,
int sizey,
int space_type,
+ bool toplevel,
bool dialog,
bool temp,
WindowAlignment alignment);
@@ -200,6 +206,13 @@ void WM_autosave_init(struct wmWindowManager *wm);
bool WM_recover_last_session(struct bContext *C, struct ReportList *reports);
void WM_file_tag_modified(void);
+struct ID *WM_file_link_datablock(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer,
+ struct View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name);
struct ID *WM_file_append_datablock(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
@@ -861,6 +874,7 @@ int WM_event_modifier_flag(const struct wmEvent *event);
bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
bool WM_event_is_last_mousemove(const struct wmEvent *event);
+bool WM_event_is_mouse_drag(const struct wmEvent *event);
int WM_event_drag_threshold(const struct wmEvent *event);
bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]);
@@ -928,7 +942,7 @@ void WM_generic_user_data_free(struct wmGenericUserData *wm_userdata);
bool WM_region_use_viewport(struct ScrArea *area, struct ARegion *region);
#ifdef WITH_XR_OPENXR
-/* wm_xr.c */
+/* wm_xr_session.c */
bool WM_xr_session_exists(const wmXrData *xr);
bool WM_xr_session_is_ready(const wmXrData *xr);
struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr);
@@ -938,7 +952,74 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro
bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
float r_viewmat[4][4],
float *r_focal_len);
-#endif
+bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3]);
+bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4]);
+
+/* wm_xr_actions.c */
+/* XR action functions to be called pre-XR session start.
+ * Note: The "destroy" functions can also be called post-session start. */
+bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name);
+void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name);
+bool WM_xr_action_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ struct wmOperatorType *ot,
+ struct IDProperty *op_properties,
+ eXrOpFlag op_flag);
+void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name);
+bool WM_xr_action_space_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const struct wmXrPose *poses);
+void WM_xr_action_space_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths);
+bool WM_xr_action_binding_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths);
+void WM_xr_action_binding_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths);
+
+bool WM_xr_active_action_set_set(
+ wmXrData *xr, const char *action_set_name); /* If action_set_name is NULL, then
+ * all action sets will be treated as active. */
+bool WM_xr_controller_pose_action_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name);
+
+/* XR action functions to be called post-XR session start. */
+bool WM_xr_action_state_get(const wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path,
+ struct wmXrActionState *r_state);
+bool WM_xr_haptic_action_apply(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const long long *duration,
+ const float *frequency,
+ const float *amplitude);
+void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name);
+#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
}
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index d54925272de..0b26f3c54ae 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -132,17 +132,21 @@ struct wmWindowManager;
extern "C" {
#endif
+typedef void (*wmGenericUserDataFreeFn)(void *data);
+
typedef struct wmGenericUserData {
void *data;
/** When NULL, use #MEM_freeN. */
- void (*free_fn)(void *data);
+ wmGenericUserDataFreeFn free_fn;
bool use_free;
} wmGenericUserData;
+typedef void (*wmGenericCallbackFn)(struct bContext *C, void *user_data);
+
typedef struct wmGenericCallback {
- void (*exec)(struct bContext *C, void *user_data);
+ wmGenericCallbackFn exec;
void *user_data;
- void (*free_user_data)(void *user_data);
+ wmGenericUserDataFreeFn free_user_data;
} wmGenericCallback;
/* ************** wmOperatorType ************************ */
@@ -680,6 +684,25 @@ typedef struct wmNDOFMotionData {
} wmNDOFMotionData;
#endif /* WITH_INPUT_NDOF */
+#ifdef WITH_XR_OPENXR
+/* Similar to GHOST_XrPose. */
+typedef struct wmXrPose {
+ float position[3];
+ /* Blender convention (w, x, y, z) */
+ float orientation_quat[4];
+} wmXrPose;
+
+typedef struct wmXrActionState {
+ union {
+ bool state_boolean;
+ float state_float;
+ float state_vector2f[2];
+ wmXrPose state_pose;
+ };
+ int type; /* eXrActionType */
+} wmXrActionState;
+#endif
+
/** Timer flags. */
typedef enum {
/** Do not attempt to free customdata pointer even if non-NULL. */
@@ -897,6 +920,7 @@ typedef struct wmDragAsset {
/* Always freed. */
const char *path;
int id_type;
+ int import_type; /* eFileAssetImportType */
} wmDragAsset;
typedef struct wmDrag {
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
index cf1a7628267..c7a4b064d0e 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
@@ -146,6 +146,7 @@ void WM_gizmotype_append(void (*gtfunc)(struct wmGizmoType *));
void WM_gizmotype_append_ptr(void (*gtfunc)(struct wmGizmoType *, void *), void *userdata);
bool WM_gizmotype_remove(struct bContext *C, struct Main *bmain, const char *idname);
void WM_gizmotype_remove_ptr(struct bContext *C, struct Main *bmain, struct wmGizmoType *gzt);
+void WM_gizmotype_free_ptr(struct wmGizmoType *gzt);
void WM_gizmotype_iter(struct GHashIterator *ghi);
/* wm_gizmo_group_type.c */
@@ -154,8 +155,6 @@ struct wmGizmoGroupType *WM_gizmogrouptype_append(void (*wtfunc)(struct wmGizmoG
struct wmGizmoGroupType *WM_gizmogrouptype_append_ptr(void (*wtfunc)(struct wmGizmoGroupType *,
void *),
void *userdata);
-bool WM_gizmogrouptype_free(const char *idname);
-void WM_gizmogrouptype_free_ptr(struct wmGizmoGroupType *gzgt);
void WM_gizmogrouptype_iter(struct GHashIterator *ghi);
struct wmGizmoGroupTypeRef *WM_gizmogrouptype_append_and_link(
@@ -378,6 +377,9 @@ void WM_gizmo_group_unlink_delayed_ptr_from_space(struct wmGizmoGroupType *gzgt,
struct wmGizmoMapType *gzmap_type,
struct ScrArea *area);
+void WM_gizmo_group_type_free_ptr(wmGizmoGroupType *gzgt);
+bool WM_gizmo_group_type_free(const char *idname);
+
/* Has the result of unlinking and linking (re-initializes gizmo's). */
void WM_gizmo_group_type_reinit_ptr_ex(struct Main *bmain,
struct wmGizmoGroupType *gzgt,
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index 32b6a6e6b31..062731dfb3d 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -592,7 +592,7 @@ static int gizmo_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const int highlight_part_init = gz->highlight_part;
if (gz->drag_part != -1) {
- if (ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG)) {
+ if (WM_event_is_mouse_drag(event)) {
gz->highlight_part = gz->drag_part;
}
}
@@ -1058,12 +1058,14 @@ bool WM_gizmo_group_type_ensure(const char *idname)
return WM_gizmo_group_type_ensure_ptr(gzgt);
}
+/**
+ * Call #WM_gizmo_group_type_free_ptr after to remove & free.
+ */
void WM_gizmo_group_type_remove_ptr_ex(struct Main *bmain,
wmGizmoGroupType *gzgt,
wmGizmoMapType *gzmap_type)
{
WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt);
- WM_gizmogrouptype_free_ptr(gzgt);
}
void WM_gizmo_group_type_remove_ptr(struct Main *bmain, wmGizmoGroupType *gzgt)
{
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
index ab5a265547d..6ebeb5a76b6 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
@@ -154,7 +154,7 @@ static void gizmogrouptype_free(wmGizmoGroupType *gzgt)
MEM_freeN(gzgt);
}
-void WM_gizmogrouptype_free_ptr(wmGizmoGroupType *gzgt)
+void WM_gizmo_group_type_free_ptr(wmGizmoGroupType *gzgt)
{
BLI_assert(gzgt == WM_gizmogrouptype_find(gzgt->idname, false));
@@ -165,7 +165,7 @@ void WM_gizmogrouptype_free_ptr(wmGizmoGroupType *gzgt)
/* XXX, TODO, update the world! */
}
-bool WM_gizmogrouptype_free(const char *idname)
+bool WM_gizmo_group_type_free(const char *idname)
{
wmGizmoGroupType *gzgt = BLI_ghash_lookup(global_gizmogrouptype_hash, idname);
@@ -173,7 +173,7 @@ bool WM_gizmogrouptype_free(const char *idname)
return false;
}
- WM_gizmogrouptype_free_ptr(gzgt);
+ WM_gizmo_group_type_free_ptr(gzgt);
return true;
}
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 45950a32d85..2ffa04bd8ae 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -620,7 +620,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
struct GPUMatrixUnproject_Precalc unproj_precalc;
GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport);
- GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
+ GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
uint *buf_iter = buffer;
int hit_found = -1;
@@ -631,7 +631,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8];
float co_3d[3];
co_screen[2] = int_as_float(buf_iter[1]);
- GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d);
+ GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d);
float select_bias = gz->select_bias;
if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) {
select_bias *= gz->scale_final;
@@ -731,15 +731,6 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
BLI_buffer_declare_static(wmGizmo *, visible_3d_gizmos, BLI_BUFFER_NOP, 128);
bool do_step[WM_GIZMOMAP_DRAWSTEP_MAX];
- int mval[2] = {UNPACK2(event->mval)};
-
- /* Ensure for drag events we use the location where the user clicked.
- * Without this click-dragging on a gizmo can accidentally act on the wrong gizmo. */
- if (ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG)) {
- mval[0] += event->x - event->prevclickx;
- mval[1] += event->y - event->prevclicky;
- }
-
for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
do_step[i] = WM_gizmo_context_check_drawstep(C, i);
}
@@ -775,7 +766,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
}
else if (step == WM_GIZMOMAP_DRAWSTEP_2D) {
if ((gz = wm_gizmogroup_find_intersected_gizmo(
- wm, gzgroup, C, event_modifier, mval, r_part))) {
+ wm, gzgroup, C, event_modifier, event->mval, r_part))) {
break;
}
}
@@ -787,7 +778,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
/* 2D gizmos get priority. */
if (gz == NULL) {
gz = gizmo_find_intersected_3d(
- C, mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
+ C, event->mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
}
}
BLI_buffer_free(&visible_3d_gizmos);
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
index 185854b1ca0..1523246d08b 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
@@ -121,7 +121,7 @@ void WM_gizmotype_append_ptr(void (*gtfunc)(struct wmGizmoType *, void *), void
/**
* Free but don't remove from ghash.
*/
-static void gizmotype_free(wmGizmoType *gzt)
+void WM_gizmotype_free_ptr(wmGizmoType *gzt)
{
if (gzt->rna_ext.srna) { /* python gizmo, allocs own string */
MEM_freeN((void *)gzt->idname);
@@ -169,8 +169,6 @@ void WM_gizmotype_remove_ptr(bContext *C, Main *bmain, wmGizmoType *gzt)
BLI_ghash_remove(global_gizmotype_hash, gzt->idname, NULL, NULL);
gizmotype_unlink(C, bmain, gzt);
-
- gizmotype_free(gzt);
}
bool WM_gizmotype_remove(bContext *C, Main *bmain, const char *idname)
@@ -186,9 +184,9 @@ bool WM_gizmotype_remove(bContext *C, Main *bmain, const char *idname)
return true;
}
-static void wm_gizmotype_ghash_free_cb(wmGizmoType *mt)
+static void wm_gizmotype_ghash_free_cb(wmGizmoType *gzt)
{
- gizmotype_free(mt);
+ WM_gizmotype_free_ptr(gzt);
}
void wm_gizmotype_free(void)
diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
index 84e6308223f..ac4f6b761ac 100644
--- a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
+++ b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
@@ -62,9 +62,9 @@ typedef void (*wmGizmoFnMatrixBasisGet)(const struct wmGizmo *, float[4][4]);
typedef int (*wmGizmoFnInvoke)(struct bContext *, struct wmGizmo *, const struct wmEvent *);
typedef void (*wmGizmoFnExit)(struct bContext *, struct wmGizmo *, const bool);
typedef int (*wmGizmoFnCursorGet)(struct wmGizmo *);
-typedef void (*wmGizmoFnScreenBoundsGet)(struct bContext *,
+typedef bool (*wmGizmoFnScreenBoundsGet)(struct bContext *,
struct wmGizmo *,
- rcti *r_bounding_box);
+ rcti *r_bounding_box) ATTR_WARN_UNUSED_RESULT;
typedef void (*wmGizmoFnSelectRefresh)(struct wmGizmo *);
typedef void (*wmGizmoFnFree)(struct wmGizmo *);
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index ae5b6c468f7..46e47ae95c4 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -186,6 +186,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
win->addmousemove = true;
win->event_queue_check_click = 0;
win->event_queue_check_drag = 0;
+ win->event_queue_check_drag_handled = 0;
BLO_read_data_address(reader, &win->stereo3d_format);
/* Multi-view always fallback to anaglyph at file opening
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index cdb7b591907..11783ae3517 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -264,7 +264,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4])
if (wrap == WM_CURSOR_WRAP_X) {
mode_axis = GHOST_kAxisX;
}
- if (wrap == WM_CURSOR_WRAP_Y) {
+ else if (wrap == WM_CURSOR_WRAP_Y) {
mode_axis = GHOST_kGrabAxisY;
}
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 9684c21605a..fc62d0c6e06 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -26,6 +26,7 @@
#include <string.h>
#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
#include "MEM_guardedalloc.h"
@@ -372,14 +373,22 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode)
}
wmDragAsset *asset_drag = drag->poin;
- return (idcode == 0 || asset_drag->id_type == idcode) ? asset_drag : NULL;
+ return (ELEM(idcode, 0, asset_drag->id_type)) ? 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);
+ switch ((eFileAssetImportType)asset_drag->import_type) {
+ case FILE_ASSET_IMPORT_LINK:
+ return WM_file_link_datablock(
+ G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ case FILE_ASSET_IMPORT_APPEND:
+ return WM_file_append_datablock(
+ G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ }
+
+ BLI_assert_unreachable();
+ return NULL;
}
/**
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index e0c4ab8eaf3..0922aaaee53 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -218,13 +218,6 @@ static bool wm_draw_region_stereo_set(Main *bmain,
return false;
}
-static void wm_area_mark_invalid_backbuf(ScrArea *area)
-{
- if (area->spacetype == SPACE_VIEW3D) {
- ((View3D *)area->spacedata.first)->flag |= V3D_INVALID_BACKBUF;
- }
-}
-
static void wm_region_test_gizmo_do_draw(bContext *C,
ScrArea *area,
ARegion *region,
@@ -739,7 +732,6 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
}
}
- wm_area_mark_invalid_backbuf(area);
CTX_wm_area_set(C, NULL);
GPU_debug_group_end();
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 9b9be6bb497..0050c834a56 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -265,6 +265,11 @@ bool WM_event_is_last_mousemove(const wmEvent *event)
return true;
}
+bool WM_event_is_mouse_drag(const wmEvent *event)
+{
+ return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG));
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -277,15 +282,17 @@ bool WM_event_is_last_mousemove(const wmEvent *event)
int WM_event_drag_threshold(const struct wmEvent *event)
{
int drag_threshold;
- if (WM_event_is_tablet(event)) {
- drag_threshold = U.drag_threshold_tablet;
- }
- else if (ISMOUSE(event->prevtype)) {
+ if (ISMOUSE(event->prevtype)) {
BLI_assert(event->prevtype != MOUSEMOVE);
/* Using the previous type is important is we want to check the last pressed/released button,
* The `event->type` would include #MOUSEMOVE which is always the case when dragging
* and does not help us know which threshold to use. */
- drag_threshold = U.drag_threshold_mouse;
+ if (WM_event_is_tablet(event)) {
+ drag_threshold = U.drag_threshold_tablet;
+ }
+ else {
+ drag_threshold = U.drag_threshold_mouse;
+ }
}
else {
/* Typically keyboard, could be NDOF button or other less common types. */
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 791aeeb39cd..a6588c40f0f 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -580,7 +580,7 @@ void wm_event_do_notifiers(bContext *C)
if ((note->category == NC_SPACE) && note->reference) {
/* Filter out notifiers sent to other spaces. RNA sets the reference to the owning ID
* though, the screen, so let notifiers through that reference the entire screen. */
- if ((note->reference != area->spacedata.first) && (note->reference != screen)) {
+ if (!ELEM(note->reference, area->spacedata.first, screen)) {
continue;
}
}
@@ -1048,7 +1048,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
wmWindowManager *wm = CTX_wm_manager(C);
int retval = OPERATOR_CANCELLED;
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
if (op == NULL || op->type == NULL) {
return retval;
@@ -1469,7 +1469,7 @@ static int wm_operator_call_internal(bContext *C,
{
int retval;
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
/* Dummy test. */
if (ot) {
@@ -1621,7 +1621,7 @@ int WM_operator_name_call_with_properties(struct bContext *C,
{
PointerRNA props_ptr;
wmOperatorType *ot = WM_operatortype_find(opstring, false);
- RNA_pointer_create(NULL, ot->srna, properties, &props_ptr);
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, properties, &props_ptr);
return WM_operator_name_call_ptr(C, ot, context, &props_ptr);
}
@@ -2595,6 +2595,15 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
ListBase *handlers,
const bool do_debug_handler)
{
+ /* Drag events use the previous click location to highlight the gizmos,
+ * Get the highlight again in case the user dragged off the gizmo. */
+ const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
+ const bool is_event_modifier = ISKEYMODIFIER(event->type);
+ /* Only keep the highlight if the gizmo becomes modal as result of event handling.
+ * Without this check, even un-handled drag events will set the highlight if the drag
+ * was initiated over a gizmo. */
+ const bool restore_highlight_unless_activated = is_event_drag;
+
int action = WM_HANDLER_CONTINUE;
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
@@ -2608,13 +2617,25 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
if (region->type->clip_gizmo_events_by_ui) {
if (UI_region_block_find_mouse_over(region, &event->x, true)) {
if (gz != NULL && event->type != EVT_GIZMO_UPDATE) {
- WM_tooltip_clear(C, CTX_wm_window(C));
- wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
+ if (restore_highlight_unless_activated == false) {
+ WM_tooltip_clear(C, CTX_wm_window(C));
+ wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
+ }
}
return action;
}
}
+ struct {
+ wmGizmo *gz_modal;
+ wmGizmo *gz;
+ int part;
+ } prev = {
+ .gz_modal = wm_gizmomap_modal_get(gzmap),
+ .gz = gz,
+ .part = gz ? gz->highlight_part : 0,
+ };
+
if (region->gizmo_map != handler->gizmo_map) {
WM_gizmomap_tag_refresh(handler->gizmo_map);
}
@@ -2622,16 +2643,11 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
wm_gizmomap_handler_context_gizmo(C, handler);
wm_region_mouse_co(C, event);
- /* Drag events use the previous click location to highlight the gizmos,
- * Get the highlight again in case the user dragged off the gizmo. */
- const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
- const bool is_event_modifier = ISKEYMODIFIER(event->type);
-
bool handle_highlight = false;
bool handle_keymap = false;
/* Handle gizmo highlighting. */
- if (!wm_gizmomap_modal_get(gzmap) &&
+ if ((prev.gz_modal == NULL) &&
((event->type == MOUSEMOVE) || is_event_modifier || is_event_drag)) {
handle_highlight = true;
if (is_event_modifier || is_event_drag) {
@@ -2642,14 +2658,15 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
handle_keymap = true;
}
+ /* There is no need to handle this event when the key-map isn't being applied
+ * since any change to the highlight will be restored to the previous value. */
+ if (restore_highlight_unless_activated) {
+ if ((handle_highlight == true) && (handle_keymap == false)) {
+ return action;
+ }
+ }
+
if (handle_highlight) {
- struct {
- wmGizmo *gz;
- int part;
- } prev = {
- .gz = gz,
- .part = gz ? gz->highlight_part : 0,
- };
int part = -1;
gz = wm_gizmomap_highlight_find(gzmap, C, event, &part);
@@ -2734,6 +2751,16 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
}
}
+ if (handle_highlight) {
+ if (restore_highlight_unless_activated) {
+ /* Check handling the key-map didn't activate a gizmo. */
+ wmGizmo *gz_modal = wm_gizmomap_modal_get(gzmap);
+ if (!(gz_modal && (gz_modal != prev.gz_modal))) {
+ wm_gizmomap_highlight_set(gzmap, C, prev.gz, prev.part);
+ }
+ }
+ }
+
if (is_event_handle_all) {
if (action == WM_HANDLER_CONTINUE) {
action |= WM_HANDLER_BREAK | WM_HANDLER_MODAL;
@@ -2941,6 +2968,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
if (wm_action_not_handled(action)) {
if (win->event_queue_check_drag) {
if (WM_event_drag_test(event, &event->prevclickx)) {
+ win->event_queue_check_drag_handled = true;
+
int x = event->x;
int y = event->y;
short val = event->val;
@@ -2984,6 +3013,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
if (event->is_repeat == false) {
win->event_queue_check_click = true;
win->event_queue_check_drag = true;
+ win->event_queue_check_drag_handled = false;
}
}
else if (event->val == KM_RELEASE) {
@@ -3470,6 +3500,13 @@ void wm_event_do_handlers(bContext *C)
win->event_queue_check_click = false;
}
+ /* If the drag even was handled, don't attempt to keep re-handing the same
+ * drag event on every cursor motion, see: T87511. */
+ if (win->event_queue_check_drag_handled) {
+ win->event_queue_check_drag = false;
+ win->event_queue_check_drag_handled = false;
+ }
+
/* Update previous mouse position for following events to use. */
win->eventstate->prevx = event->x;
win->eventstate->prevy = event->y;
@@ -4441,16 +4478,21 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event.prevtype = event.type;
event.prevval = event.val;
- /* Ensure the event state is correct, any deviation from this may cause bugs. */
+ /* Ensure the event state is correct, any deviation from this may cause bugs.
+ *
+ * NOTE: #EVENT_NONE is set when unknown keys are pressed,
+ * while not common, avoid a false alarm. */
#ifndef NDEBUG
if ((event_state->type || event_state->val) && /* Ignore cleared event state. */
- !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type))) {
+ !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type) ||
+ (event_state->type == EVENT_NONE))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->type = %d'",
event_state->type);
}
if ((event_state->prevtype || event_state->prevval) && /* Ignore cleared event state. */
- !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype))) {
+ !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype) ||
+ (event_state->type == EVENT_NONE))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'",
event_state->prevtype);
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index bbcb0669cce..0aff2c00644 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -911,7 +911,10 @@ void wm_homefile_read(bContext *C,
const char *app_template_override,
bool *r_is_factory_startup)
{
- Main *bmain = G_MAIN; /* Context does not always have valid main pointer here... */
+#if 0 /* UNUSED, keep as this may be needed later & the comment below isn't self evident. */
+ /* Context does not always have valid main pointer here. */
+ Main *bmain = G_MAIN;
+#endif
ListBase wmbase;
bool success = false;
@@ -1170,7 +1173,7 @@ void wm_homefile_read(bContext *C,
BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template));
}
- bmain = CTX_data_main(C);
+ Main *bmain = CTX_data_main(C);
if (use_userdef) {
/* check userdef before open window, keymaps etc */
@@ -2157,21 +2160,9 @@ static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
}
-static void wm_free_operator_properties_callback(void *user_data)
-{
- IDProperty *properties = (IDProperty *)user_data;
- IDP_FreeProperty(properties);
-}
-
static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (U.uiflag & USER_SAVE_PROMPT &&
- wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
- wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
- callback->exec = wm_homefile_read_after_dialog_callback;
- callback->user_data = IDP_CopyProperty(op->properties);
- callback->free_user_data = wm_free_operator_properties_callback;
- wm_close_file_dialog(C, callback);
+ if (wm_operator_close_file_dialog_if_needed(C, op, wm_homefile_read_after_dialog_callback)) {
return OPERATOR_INTERFACE;
}
return wm_homefile_read_exec(C, op);
@@ -2331,13 +2322,7 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
}
- if (U.uiflag & USER_SAVE_PROMPT &&
- wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
- wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
- callback->exec = wm_open_mainfile_after_dialog_callback;
- callback->user_data = IDP_CopyProperty(op->properties);
- callback->free_user_data = wm_free_operator_properties_callback;
- wm_close_file_dialog(C, callback);
+ if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) {
return OPERATOR_INTERFACE;
}
return wm_open_mainfile_dispatch(C, op);
@@ -2500,12 +2485,11 @@ static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op)
{
struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
uiLayout *layout = op->layout;
- uiLayout *col = op->layout;
const char *autoexec_text;
uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE);
- col = uiLayoutColumn(layout, false);
+ uiLayout *col = uiLayoutColumn(layout, false);
if (file_info->is_untrusted) {
autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
uiLayoutSetActive(col, false);
@@ -2637,12 +2621,25 @@ static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static int wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data)
+{
+ WM_operator_name_call_with_properties(
+ C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
+}
+
+static int wm_recover_last_session_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
{
/* Keep the current setting instead of using the preferences since a file selector
* doesn't give us the option to change the setting. */
wm_open_init_use_scripts(op, false);
- return WM_operator_confirm(C, op, event);
+
+ if (wm_operator_close_file_dialog_if_needed(
+ C, op, wm_recover_last_session_after_dialog_callback)) {
+ return OPERATOR_INTERFACE;
+ }
+ return wm_recover_last_session_exec(C, op);
}
void WM_OT_recover_last_session(wmOperatorType *ot)
@@ -3270,7 +3267,10 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat
bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
if (file_has_been_saved_before) {
- WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL);
+ if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL) &
+ OPERATOR_CANCELLED) {
+ execute_callback = false;
+ }
}
else {
WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL);
@@ -3456,4 +3456,31 @@ void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
}
}
+static void wm_free_operator_properties_callback(void *user_data)
+{
+ IDProperty *properties = (IDProperty *)user_data;
+ IDP_FreeProperty(properties);
+}
+
+/**
+ * \return True if the dialog was created, the calling operator should return #OPERATOR_INTERFACE
+ * then.
+ */
+bool wm_operator_close_file_dialog_if_needed(bContext *C,
+ wmOperator *op,
+ wmGenericCallbackFn post_action_fn)
+{
+ if (U.uiflag & USER_SAVE_PROMPT &&
+ wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
+ wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
+ callback->exec = post_action_fn;
+ callback->user_data = IDP_CopyProperty(op->properties);
+ callback->free_user_data = wm_free_operator_properties_callback;
+ wm_close_file_dialog(C, callback);
+ return true;
+ }
+
+ return false;
+}
+
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 840debad01b..e467dcd243e 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -642,23 +642,23 @@ void WM_OT_append(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Append Single Data-Block & Return it
+/** \name Link/Append Single Data-Block & Return it
*
- * Used for appending workspace from startup files.
* \{ */
-ID *WM_file_append_datablock(Main *bmain,
- Scene *scene,
- ViewLayer *view_layer,
- View3D *v3d,
- const char *filepath,
- const short id_code,
- const char *id_name)
+static ID *wm_file_link_datablock_ex(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name,
+ bool clear_pre_existing_flag)
{
/* Tag everything so we can make local only the new datablock. */
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
- /* Define working data, with just the one item we want to append. */
+ /* Define working data, with just the one item we want to link. */
WMLinkAppendData *lapp_data = wm_link_append_data_new(0);
wm_link_append_data_library_add(lapp_data, filepath);
@@ -672,6 +672,36 @@ ID *WM_file_append_datablock(Main *bmain,
ID *id = item->new_id;
wm_link_append_data_free(lapp_data);
+ if (clear_pre_existing_flag) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
+ }
+
+ return id;
+}
+
+ID *WM_file_link_datablock(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name)
+{
+ return wm_file_link_datablock_ex(
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, true);
+}
+
+ID *WM_file_append_datablock(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name)
+{
+ ID *id = wm_file_link_datablock_ex(
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, false);
+
/* Make datablock local. */
BKE_library_make_local(bmain, NULL, NULL, true, false);
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 4c1b403ac96..ae2cdb5608c 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -385,7 +385,7 @@ static void draw_filled_lasso(wmGesture *gt)
mcoords[i][1] = lasso[1];
}
- BLI_lasso_boundbox(&rect, (const int(*)[2])mcoords, mcoords_len);
+ BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin);
BLI_rcti_isect(&gt->winrct, &rect, &rect);
@@ -402,7 +402,7 @@ static void draw_filled_lasso(wmGesture *gt)
rect.ymin,
rect.xmax,
rect.ymax,
- (const int(*)[2])mcoords,
+ mcoords,
mcoords_len,
draw_filled_lasso_px_cb,
&lasso_fill_data);
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 07d68293714..94535427dac 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -316,6 +316,7 @@ int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (gesture->wait_for_input == false) {
gesture->is_active = true;
gesture_circle_apply(C, op);
+ gesture->is_active_prev = true;
}
/* add modal handler */
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 56fd51ac6fd..15f0978f87d 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -343,8 +343,13 @@ void WM_init(bContext *C, int argc, const char **argv)
(void)argv; /* unused */
#endif
- if (!G.background && !wm_start_with_console) {
- GHOST_toggleConsole(3);
+ if (!G.background) {
+ if (wm_start_with_console) {
+ GHOST_toggleConsole(1);
+ }
+ else {
+ GHOST_toggleConsole(3);
+ }
}
BKE_material_copybuf_clear();
@@ -496,7 +501,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
if ((has_edited &&
BLO_write_file(
bmain, filename, fileflags, &(const struct BlendFileWriteParams){0}, NULL)) ||
- (undo_memfile && BLO_memfile_write_file(undo_memfile, filename))) {
+ (BLO_memfile_write_file(undo_memfile, filename))) {
printf("Saved session recovery to '%s'\n", filename);
}
}
diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c
index 38d06ea83d3..0a157e63b09 100644
--- a/source/blender/windowmanager/intern/wm_keymap.c
+++ b/source/blender/windowmanager/intern/wm_keymap.c
@@ -80,6 +80,9 @@ static wmKeyMapItem *wm_keymap_item_copy(wmKeyMapItem *kmi)
kmin->ptr = MEM_callocN(sizeof(PointerRNA), "UserKeyMapItemPtr");
WM_operator_properties_create(kmin->ptr, kmin->idname);
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmin->ptr->owner_id = NULL;
+
kmin->properties = IDP_CopyProperty(kmin->properties);
kmin->ptr->data = kmin->properties;
}
@@ -106,6 +109,9 @@ static void wm_keymap_item_properties_set(wmKeyMapItem *kmi)
{
WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname);
WM_operator_properties_sanitize(kmi->ptr, 1);
+
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmi->ptr->owner_id = NULL;
}
/**
@@ -136,6 +142,9 @@ static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi)
kmi->ptr->data = kmi->properties;
}
WM_operator_properties_sanitize(kmi->ptr, 1);
+
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmi->ptr->owner_id = NULL;
}
}
else {
diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c
index 0e57a92b685..f33db7d50b5 100644
--- a/source/blender/windowmanager/intern/wm_operator_type.c
+++ b/source/blender/windowmanager/intern/wm_operator_type.c
@@ -252,7 +252,7 @@ void WM_operatortype_props_advanced_end(wmOperatorType *ot)
return;
}
- RNA_pointer_create(NULL, ot->srna, NULL, &struct_ptr);
+ WM_operator_properties_create_ptr(&struct_ptr, ot);
RNA_STRUCT_BEGIN (&struct_ptr, prop) {
counter++;
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 84c16999c1b..9175eb2dbf7 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -583,7 +583,8 @@ char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, i
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
{
- RNA_pointer_create(NULL, ot->srna, NULL, ptr);
+ /* Set the ID so the context can be accessed: see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, NULL, ptr);
}
void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
@@ -594,7 +595,8 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
WM_operator_properties_create_ptr(ptr, ot);
}
else {
- RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
+ /* Set the ID so the context can be accessed: see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ RNA_pointer_create(G_MAIN->wm.first, &RNA_OperatorProperties, NULL, ptr);
}
}
@@ -1205,7 +1207,7 @@ IDProperty *WM_operator_last_properties_ensure_idprops(wmOperatorType *ot)
void WM_operator_last_properties_ensure(wmOperatorType *ot, PointerRNA *ptr)
{
IDProperty *props = WM_operator_last_properties_ensure_idprops(ot);
- RNA_pointer_create(NULL, ot->srna, props, ptr);
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, props, ptr);
}
/**
@@ -1909,6 +1911,9 @@ static bool wm_operator_winactive_normal(bContext *C)
if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL))) {
return 0;
}
+ if (G.background) {
+ return 0;
+ }
return 1;
}
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 58030920fc7..5300649a0cd 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -47,9 +47,11 @@
#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
+#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -95,64 +97,85 @@ static AUD_Device *audio_device = NULL;
struct PlayState;
static void playanim_window_zoom(struct PlayState *ps, const float zoom_offset);
+/**
+ * The current state of the player.
+ *
+ * \warning Don't store results of parsing command-line arguments
+ * in this struct if they need to persist across playing back different
+ * files as these will be cleared when playing other files (drag & drop).
+ */
typedef struct PlayState {
- /* window and viewport size */
+ /** Window and viewport size. */
int win_x, win_y;
- /* current zoom level */
+ /** Current zoom level. */
float zoom;
- /* playback state */
+ /** Playback direction (-1, 1). */
short direction;
+ /** Set the next frame to implement frame stepping (using shortcuts). */
short next_frame;
+ /** Playback once then wait. */
bool once;
- bool turbo;
+ /** Play forwards/backwards. */
bool pingpong;
+ /** Disable frame skipping. */
bool noskip;
+ /** Display current frame over the window. */
bool indicator;
+ /** Single-frame stepping has been enabled (frame loading and update pending). */
bool sstep;
+ /** Playback has stopped the image has been displayed. */
bool wait2;
+ /** Playback stopped state once stop/start variables have been handled. */
bool stopped;
+ /**
+ * When disabled the current animation will exit,
+ * after this either the application exits or a new animation window is opened.
+ *
+ * This is used so drag & drop can load new files which setup a newly created animation window.
+ */
bool go;
- /* waiting for images to load */
+ /** True when waiting for images to load. */
bool loading;
- /* x/y image flip */
+ /** X/Y image flip (set via key bindings). */
bool draw_flip[2];
+ /** The number of frames to step each update (default to 1, command line argument). */
int fstep;
- /* current picture */
+ /** Current frame (picture). */
struct PlayAnimPict *picture;
- /* set once at the start */
+ /** Image size in pixels, set once at the start. */
int ibufx, ibufy;
+ /** Mono-space font ID. */
int fontid;
- /* saves passing args */
- struct ImBuf *curframe_ibuf;
-
- /* restarts player for file drop */
+ /** Restarts player for file drop (drag & drop). */
char dropped_file[FILE_MAX];
+ /** Force update when scrubbing with the cursor. */
bool need_frame_update;
+ /** The current frame calculated by scrubbing the mouse cursor. */
int frame_cursor_x;
+
+ ColorManagedViewSettings view_settings;
+ ColorManagedDisplaySettings display_settings;
} PlayState;
/* for debugging */
#if 0
-void print_ps(PlayState *ps)
+static void print_ps(PlayState *ps)
{
printf("ps:\n");
printf(" direction=%d,\n", (int)ps->direction);
- printf(" next=%d,\n", ps->next);
printf(" once=%d,\n", ps->once);
- printf(" turbo=%d,\n", ps->turbo);
printf(" pingpong=%d,\n", ps->pingpong);
printf(" noskip=%d,\n", ps->noskip);
printf(" sstep=%d,\n", ps->sstep);
- printf(" pause=%d,\n", ps->pause);
printf(" wait2=%d,\n", ps->wait2);
printf(" stopped=%d,\n", ps->stopped);
printf(" go=%d,\n\n", ps->go);
@@ -237,6 +260,12 @@ typedef struct PlayAnimPict {
struct anim *anim;
int frame;
int IB_flags;
+
+#ifdef USE_FRAME_CACHE_LIMIT
+ /** Back pointer to the #LinkData node for this struct in the #g_frame_cache.pics list. */
+ LinkData *frame_cache_node;
+ size_t size_in_memory;
+#endif
} PlayAnimPict;
static struct ListBase picsbase = {NULL, NULL};
@@ -248,9 +277,103 @@ static double fps_movie;
#endif
#ifdef USE_FRAME_CACHE_LIMIT
-static struct ListBase inmempicsbase = {NULL, NULL};
-static int added_images = 0;
-#endif
+static struct {
+ /** A list of #LinkData nodes referencing #PlayAnimPict to track cached frames. */
+ struct ListBase pics;
+ /** Number if elements in `pics`. */
+ int pics_len;
+ /** Keep track of memory used by #g_frame_cache.pics when `g_frame_cache.memory_limit != 0`. */
+ size_t pics_size_in_memory;
+ /** Optionally limit the amount of memory used for cache (in bytes), ignored when zero. */
+ size_t memory_limit;
+} g_frame_cache = {
+ .pics = {NULL, NULL},
+ .pics_len = 0,
+ .pics_size_in_memory = 0,
+ .memory_limit = 0,
+};
+
+static void frame_cache_add(PlayAnimPict *pic)
+{
+ pic->frame_cache_node = BLI_genericNodeN(pic);
+ BLI_addhead(&g_frame_cache.pics, pic->frame_cache_node);
+ g_frame_cache.pics_len++;
+
+ if (g_frame_cache.memory_limit != 0) {
+ BLI_assert(pic->size_in_memory == 0);
+ pic->size_in_memory = IMB_get_size_in_memory(pic->ibuf);
+ g_frame_cache.pics_size_in_memory += pic->size_in_memory;
+ }
+}
+
+static void frame_cache_remove(PlayAnimPict *pic)
+{
+ LinkData *node = pic->frame_cache_node;
+ IMB_freeImBuf(pic->ibuf);
+ if (g_frame_cache.memory_limit != 0) {
+ BLI_assert(pic->size_in_memory != 0);
+ g_frame_cache.pics_size_in_memory -= pic->size_in_memory;
+ pic->size_in_memory = 0;
+ }
+ pic->ibuf = NULL;
+ pic->frame_cache_node = NULL;
+ BLI_freelinkN(&g_frame_cache.pics, node);
+ g_frame_cache.pics_len--;
+}
+
+/* Don't free the current frame by moving it to the head of the list. */
+static void frame_cache_touch(PlayAnimPict *pic)
+{
+ BLI_assert(pic->frame_cache_node->data == pic);
+ BLI_remlink(&g_frame_cache.pics, pic->frame_cache_node);
+ BLI_addhead(&g_frame_cache.pics, pic->frame_cache_node);
+}
+
+static bool frame_cache_limit_exceeded(void)
+{
+ return g_frame_cache.memory_limit ?
+ (g_frame_cache.pics_size_in_memory > g_frame_cache.memory_limit) :
+ (g_frame_cache.pics_len > PLAY_FRAME_CACHE_MAX);
+}
+
+static void frame_cache_limit_apply(ImBuf *ibuf_keep)
+{
+ /* Really basic memory conservation scheme. Keep frames in a FIFO queue. */
+ LinkData *node = g_frame_cache.pics.last;
+ while (node && frame_cache_limit_exceeded()) {
+ PlayAnimPict *pic = node->data;
+ BLI_assert(pic->frame_cache_node == node);
+
+ node = node->prev;
+ if (pic->ibuf && pic->ibuf != ibuf_keep) {
+ frame_cache_remove(pic);
+ }
+ }
+}
+
+#endif /* USE_FRAME_CACHE_LIMIT */
+
+static ImBuf *ibuf_from_picture(PlayAnimPict *pic)
+{
+ ImBuf *ibuf = NULL;
+
+ if (pic->ibuf) {
+ ibuf = pic->ibuf;
+ }
+ else if (pic->anim) {
+ ibuf = IMB_anim_absolute(pic->anim, pic->frame, IMB_TC_NONE, IMB_PROXY_NONE);
+ }
+ else if (pic->mem) {
+ /* use correct colorspace here */
+ ibuf = IMB_ibImageFromMemory(pic->mem, pic->size, pic->IB_flags, NULL, pic->name);
+ }
+ else {
+ /* use correct colorspace here */
+ ibuf = IMB_loadiffname(pic->name, pic->IB_flags, NULL);
+ }
+
+ return ibuf;
+}
static PlayAnimPict *playanim_step(PlayAnimPict *playanim, int step)
{
@@ -278,6 +401,151 @@ static int pupdate_time(void)
return (ptottime < 0);
}
+static void *ocio_transform_ibuf(PlayState *ps,
+ ImBuf *ibuf,
+ bool *r_glsl_used,
+ eGPUTextureFormat *r_format,
+ eGPUDataFormat *r_data,
+ void **r_buffer_cache_handle)
+{
+ void *display_buffer;
+ bool force_fallback = false;
+ *r_glsl_used = false;
+ force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
+ force_fallback |= (ibuf->dither != 0.0f);
+
+ /* Default */
+ *r_format = GPU_RGBA8;
+ *r_data = GPU_DATA_UBYTE;
+
+ /* Fallback to CPU based color space conversion. */
+ if (force_fallback) {
+ *r_glsl_used = false;
+ display_buffer = NULL;
+ }
+ else if (ibuf->rect_float) {
+ display_buffer = ibuf->rect_float;
+
+ *r_data = GPU_DATA_FLOAT;
+ if (ibuf->channels == 4) {
+ *r_format = GPU_RGBA16F;
+ }
+ else if (ibuf->channels == 3) {
+ /* Alpha is implicitly 1. */
+ *r_format = GPU_RGB16F;
+ }
+
+ if (ibuf->float_colorspace) {
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space(&ps->view_settings,
+ &ps->display_settings,
+ ibuf->float_colorspace,
+ ibuf->dither,
+ false,
+ false);
+ }
+ else {
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw(
+ &ps->view_settings, &ps->display_settings, ibuf->dither, false);
+ }
+ }
+ else if (ibuf->rect) {
+ display_buffer = ibuf->rect;
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space(&ps->view_settings,
+ &ps->display_settings,
+ ibuf->rect_colorspace,
+ ibuf->dither,
+ false,
+ false);
+ }
+ else {
+ display_buffer = NULL;
+ }
+
+ /* There is data to be displayed, but GLSL is not initialized
+ * properly, in this case we fallback to CPU-based display transform. */
+ if ((ibuf->rect || ibuf->rect_float) && !*r_glsl_used) {
+ display_buffer = IMB_display_buffer_acquire(
+ ibuf, &ps->view_settings, &ps->display_settings, r_buffer_cache_handle);
+ *r_format = GPU_RGBA8;
+ *r_data = GPU_DATA_UBYTE;
+ }
+
+ return display_buffer;
+}
+
+static void draw_display_buffer(PlayState *ps, ImBuf *ibuf)
+{
+ void *display_buffer;
+
+ /* Format needs to be created prior to any #immBindShader call.
+ * Do it here because OCIO binds its own shader. */
+ eGPUTextureFormat format;
+ eGPUDataFormat data;
+ bool glsl_used = false;
+ GPUVertFormat *imm_format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint texCoord = GPU_vertformat_attr_add(
+ imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+ void *buffer_cache_handle = NULL;
+ display_buffer = ocio_transform_ibuf(ps, ibuf, &glsl_used, &format, &data, &buffer_cache_handle);
+
+ GPUTexture *texture = GPU_texture_create_2d("display_buf", ibuf->x, ibuf->y, 1, format, NULL);
+ GPU_texture_update(texture, data, display_buffer);
+ GPU_texture_filter_mode(texture, false);
+
+ GPU_texture_bind(texture, 0);
+
+ if (!glsl_used) {
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immUniformColor3f(1.0f, 1.0f, 1.0f);
+ immUniform1i("image", 0);
+ }
+
+ immBegin(GPU_PRIM_TRI_FAN, 4);
+
+ rctf preview;
+ rctf canvas;
+
+ BLI_rctf_init(&canvas, 0.0f, 1.0f, 0.0f, 1.0f);
+ BLI_rctf_init(&preview, 0.0f, 1.0f, 0.0f, 1.0f);
+
+ if (ps->draw_flip[0]) {
+ SWAP(float, canvas.xmin, canvas.xmax);
+ }
+ if (ps->draw_flip[1]) {
+ SWAP(float, canvas.ymin, canvas.ymax);
+ }
+
+ immAttr2f(texCoord, canvas.xmin, canvas.ymin);
+ immVertex2f(pos, preview.xmin, preview.ymin);
+
+ immAttr2f(texCoord, canvas.xmin, canvas.ymax);
+ immVertex2f(pos, preview.xmin, preview.ymax);
+
+ immAttr2f(texCoord, canvas.xmax, canvas.ymax);
+ immVertex2f(pos, preview.xmax, preview.ymax);
+
+ immAttr2f(texCoord, canvas.xmax, canvas.ymin);
+ immVertex2f(pos, preview.xmax, preview.ymin);
+
+ immEnd();
+
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
+
+ if (!glsl_used) {
+ immUnbindProgram();
+ }
+ else {
+ IMB_colormanagement_finish_glsl_draw();
+ }
+
+ if (buffer_cache_handle) {
+ IMB_display_buffer_release(buffer_cache_handle);
+ }
+}
+
static void playanim_toscreen(
PlayState *ps, PlayAnimPict *picture, struct ImBuf *ibuf, int fontid, int fstep)
{
@@ -285,13 +553,6 @@ static void playanim_toscreen(
printf("%s: no ibuf for picture '%s'\n", __func__, picture ? picture->name : "<NIL>");
return;
}
- if (ibuf->rect == NULL && ibuf->rect_float) {
- IMB_rect_from_float(ibuf);
- imb_freerectfloatImBuf(ibuf);
- }
- if (ibuf->rect == NULL) {
- return;
- }
GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
@@ -321,19 +582,7 @@ static void playanim_toscreen(
8);
}
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
-
- immDrawPixelsTex(&state,
- offs_x + (ps->draw_flip[0] ? span_x : 0.0f),
- offs_y + (ps->draw_flip[1] ? span_y : 0.0f),
- ibuf->x,
- ibuf->y,
- GPU_RGBA8,
- false,
- ibuf->rect,
- ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x),
- ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y),
- NULL);
+ draw_display_buffer(ps, ibuf);
GPU_blend(GPU_BLEND_NONE);
@@ -413,6 +662,14 @@ static void build_pict_list_ex(
}
}
else {
+ /* Load images into cache until the cache is full,
+ * this resolves choppiness for images that are slow to load, see: T81751. */
+#ifdef USE_FRAME_CACHE_LIMIT
+ bool fill_cache = true;
+#else
+ bool fill_cache = false;
+#endif
+
int count = 0;
int fp_framenr;
@@ -441,7 +698,7 @@ static void build_pict_list_ex(
*/
while (IMB_ispic(filepath) && totframes) {
- bool hasevent;
+ bool has_event;
size_t size;
int file;
@@ -499,22 +756,33 @@ static void build_pict_list_ex(
pupdate_time();
- if (ptottime > 1.0) {
+ const bool display_imbuf = ptottime > 1.0;
+
+ if (display_imbuf || fill_cache) {
/* OCIO_TODO: support different input color space */
- struct ImBuf *ibuf;
- if (picture->mem) {
- ibuf = IMB_ibImageFromMemory(
- picture->mem, picture->size, picture->IB_flags, NULL, picture->name);
- }
- else {
- ibuf = IMB_loadiffname(picture->name, picture->IB_flags, NULL);
- }
+ ImBuf *ibuf = ibuf_from_picture(picture);
+
if (ibuf) {
- playanim_toscreen(ps, picture, ibuf, fontid, fstep);
- IMB_freeImBuf(ibuf);
+ if (display_imbuf) {
+ playanim_toscreen(ps, picture, ibuf, fontid, fstep);
+ }
+#ifdef USE_FRAME_CACHE_LIMIT
+ if (fill_cache) {
+ picture->ibuf = ibuf;
+ frame_cache_add(picture);
+ fill_cache = !frame_cache_limit_exceeded();
+ }
+ else
+#endif
+ {
+ IMB_freeImBuf(ibuf);
+ }
+ }
+
+ if (display_imbuf) {
+ pupdate_time();
+ ptottime = 0.0;
}
- pupdate_time();
- ptottime = 0.0;
}
/* create a new filepath each time */
@@ -522,7 +790,7 @@ static void build_pict_list_ex(
BLI_path_sequence_encode(
filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr);
- while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) {
+ while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) {
GHOST_DispatchEvents(g_WS.ghost_system);
if (ps->loading == false) {
return;
@@ -622,16 +890,14 @@ static void change_frame(PlayState *ps)
static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
{
PlayState *ps = (PlayState *)ps_void;
- GHOST_TEventType type = GHOST_GetEventType(evt);
- int val;
+ const GHOST_TEventType type = GHOST_GetEventType(evt);
+ /* Convert ghost event into value keyboard or mouse. */
+ const int val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
// print_ps(ps);
playanim_event_qual_update();
- /* convert ghost event into value keyboard or mouse */
- val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
-
/* first check if we're busy loading files */
if (ps->loading) {
switch (type) {
@@ -655,8 +921,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
return 1;
}
- if (ps->wait2 && ps->stopped) {
- ps->stopped = false;
+ if (ps->wait2 && ps->stopped == false) {
+ ps->stopped = true;
}
if (ps->wait2) {
@@ -815,9 +1081,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
case GHOST_kKeyNumpadSlash:
if (val) {
if (g_WS.qual & WS_QUAL_SHIFT) {
- if (ps->curframe_ibuf) {
+ if (ps->picture && ps->picture->ibuf) {
printf(" Name: %s | Speed: %.2f frames/s\n",
- ps->curframe_ibuf->name,
+ ps->picture->ibuf->name,
ps->fstep / swaptime);
}
}
@@ -1054,7 +1320,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
playanim_gl_matrix();
ptottime = 0.0;
- playanim_toscreen(ps, ps->picture, ps->curframe_ibuf, ps->fontid, ps->fstep);
+ playanim_toscreen(
+ ps, ps->picture, ps->picture ? ps->picture->ibuf : NULL, ps->fontid, ps->fstep);
break;
}
@@ -1131,7 +1398,9 @@ static void playanim_window_zoom(PlayState *ps, const float zoom_offset)
GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey);
}
-/* return path for restart */
+/**
+ * \return The a path used to restart the animation player or NULL to exit.
+ */
static char *wm_main_playanim_intern(int argc, const char **argv)
{
struct ImBuf *ibuf = NULL;
@@ -1150,7 +1419,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.direction = true;
ps.next_frame = 1;
ps.once = false;
- ps.turbo = false;
ps.pingpong = false;
ps.noskip = false;
ps.sstep = false;
@@ -1168,6 +1436,11 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.fontid = -1;
+ STRNCPY(ps.display_settings.display_device,
+ IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE));
+ IMB_colormanagement_init_default_view_settings(&ps.view_settings, &ps.display_settings);
+
+ /* Skip the first argument which is assumed to be '-a' (used to launch this player). */
while (argc > 1) {
if (argv[1][0] == '-') {
switch (argv[1][1]) {
@@ -1222,6 +1495,15 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
argc--;
argv++;
break;
+ case 'c': {
+#ifdef USE_FRAME_CACHE_LIMIT
+ const int memory_in_mb = max_ii(0, atoi(argv[2]));
+ g_frame_cache.memory_limit = (size_t)memory_in_mb * (1024 * 1024);
+#endif
+ argc--;
+ argv++;
+ break;
+ }
default:
printf("unknown option '%c': skipping\n", argv[1][1]);
break;
@@ -1389,60 +1671,29 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
#endif
while (ps.picture) {
- int hasevent;
+ bool has_event;
#ifndef USE_IMB_CACHE
if (ibuf != NULL && ibuf->ftype == IMB_FTYPE_NONE) {
IMB_freeImBuf(ibuf);
}
#endif
- if (ps.picture->ibuf) {
- ibuf = ps.picture->ibuf;
- }
- else if (ps.picture->anim) {
- ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
- }
- else if (ps.picture->mem) {
- /* use correct colorspace here */
- ibuf = IMB_ibImageFromMemory(
- ps.picture->mem, ps.picture->size, ps.picture->IB_flags, NULL, ps.picture->name);
- }
- else {
- /* use correct colorspace here */
- ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL);
- }
- if (ibuf) {
-#ifdef USE_FRAME_CACHE_LIMIT
- LinkData *node;
-#endif
+ ibuf = ibuf_from_picture(ps.picture);
+ if (ibuf) {
#ifdef USE_IMB_CACHE
ps.picture->ibuf = ibuf;
#endif
#ifdef USE_FRAME_CACHE_LIMIT
- /* Really basic memory conservation scheme. Keep frames in a FIFO queue. */
- node = inmempicsbase.last;
-
- while (node && added_images > PLAY_FRAME_CACHE_MAX) {
- PlayAnimPict *pic = node->data;
-
- if (pic->ibuf && pic->ibuf != ibuf) {
- LinkData *node_tmp;
- IMB_freeImBuf(pic->ibuf);
- pic->ibuf = NULL;
- node_tmp = node->prev;
- BLI_freelinkN(&inmempicsbase, node);
- added_images--;
- node = node_tmp;
- }
- else {
- node = node->prev;
- }
+ if (ps.picture->frame_cache_node == NULL) {
+ frame_cache_add(ps.picture);
+ }
+ else {
+ frame_cache_touch(ps.picture);
}
+ frame_cache_limit_apply(ibuf);
- BLI_addhead(&inmempicsbase, BLI_genericNodeN(ps.picture));
- added_images++;
#endif /* USE_FRAME_CACHE_LIMIT */
BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
@@ -1474,14 +1725,14 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.next_frame = ps.direction;
- while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) {
+ while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) {
GHOST_DispatchEvents(g_WS.ghost_system);
}
if (ps.go == false) {
break;
}
change_frame(&ps);
- if (!hasevent) {
+ if (!has_event) {
PIL_sleep_ms(1);
}
if (ps.wait2) {
@@ -1490,14 +1741,15 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.wait2 = ps.sstep;
- if (ps.wait2 == false && ps.stopped == false) {
- ps.stopped = true;
+ if (ps.wait2 == false && ps.stopped) {
+ ps.stopped = false;
}
pupdate_time();
if (ps.picture && ps.next_frame) {
- /* always at least set one step */
+ /* Advance to the next frame, always at least set one step.
+ * Implement frame-skipping when enabled and playback is not fast enough. */
while (ps.picture) {
ps.picture = playanim_step(ps.picture, ps.next_frame);
@@ -1510,7 +1762,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
}
}
- if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) {
+ if (ps.wait2 || ptottime < swaptime || ps.noskip) {
break;
}
ptottime -= swaptime;
@@ -1550,8 +1802,12 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
#endif
BLI_freelistN(&picsbase);
- BLI_freelistN(&inmempicsbase);
- added_images = 0;
+
+#ifdef USE_FRAME_CACHE_LIMIT
+ BLI_freelistN(&g_frame_cache.pics);
+ g_frame_cache.pics_len = 0;
+ g_frame_cache.pics_size_in_memory = 0;
+#endif
#ifdef WITH_AUDASPACE
if (playback_handle) {
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index eb1da15807c..6aedfb10dde 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -133,7 +133,7 @@ static struct WMInitStruct {
* \{ */
static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate);
-static int wm_window_timer(const bContext *C);
+static bool wm_window_timer(const bContext *C);
/* XXX this one should correctly check for apple top header...
* done for Cocoa : returns window contents (and not frame) max size*/
@@ -550,7 +550,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
}
int scr_w, scr_h;
- wm_get_screensize(&scr_w, &scr_h);
+ wm_get_desktopsize(&scr_w, &scr_h);
int posy = (scr_h - win->posy - win->sizey);
/* Clear drawable so we can set the new window. */
@@ -756,6 +756,7 @@ static bool wm_window_update_size_position(wmWindow *win)
/**
* \param space_type: SPACE_VIEW3D, SPACE_INFO, ... (eSpace_Type)
+ * \param toplevel: Not a child owned by other windows. A peer of main window.
* \param dialog: whether this should be made as a dialog-style window
* \param temp: whether this is considered a short-lived window
* \param alignment: how this window is positioned relative to its parent
@@ -768,6 +769,7 @@ wmWindow *WM_window_open(bContext *C,
int sizex,
int sizey,
int space_type,
+ bool toplevel,
bool dialog,
bool temp,
WindowAlignment alignment)
@@ -812,7 +814,7 @@ wmWindow *WM_window_open(bContext *C,
LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
if (WM_window_is_temp_screen(win_iter)) {
char *wintitle = GHOST_GetTitle(win_iter->ghostwin);
- if (strcmp(title, wintitle) == 0) {
+ if (STREQ(title, wintitle)) {
win = win_iter;
}
free(wintitle);
@@ -822,7 +824,7 @@ wmWindow *WM_window_open(bContext *C,
/* add new window? */
if (win == NULL) {
- win = wm_window_new(bmain, wm, win_prev, dialog);
+ win = wm_window_new(bmain, wm, toplevel ? NULL : win_prev, dialog);
win->posx = rect.xmin;
win->posy = rect.ymin;
*win->stereo3d_format = *win_prev->stereo3d_format;
@@ -925,6 +927,7 @@ int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op))
area->spacetype,
false,
false,
+ false,
WIN_ALIGN_PARENT_CENTER) != NULL);
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
@@ -1498,12 +1501,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
* Timer handlers should check for delta to decide if they just update, or follow real time.
* Timer handlers can also set duration to match frames passed
*/
-static int wm_window_timer(const bContext *C)
+static bool wm_window_timer(const bContext *C)
{
Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
double time = PIL_check_seconds_timer();
- int retval = 0;
+ bool has_event = false;
/* Mutable in case the timer gets removed. */
LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) {
@@ -1540,31 +1543,34 @@ static int wm_window_timer(const bContext *C)
event.customdata = wt;
wm_event_add(win, &event);
- retval = 1;
+ has_event = true;
}
}
}
- return retval;
+ return has_event;
}
void wm_window_process_events(const bContext *C)
{
BLI_assert(BLI_thread_is_main());
- int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
+ bool has_event = GHOST_ProcessEvents(g_system, false); /* `false` is no wait. */
- if (hasevent) {
+ if (has_event) {
GHOST_DispatchEvents(g_system);
}
- hasevent |= wm_window_timer(C);
+ has_event |= wm_window_timer(C);
#ifdef WITH_XR_OPENXR
/* XR events don't use the regular window queues. So here we don't only trigger
* processing/dispatching but also handling. */
- hasevent |= wm_xr_events_handle(CTX_wm_manager(C));
+ has_event |= wm_xr_events_handle(CTX_wm_manager(C));
#endif
- /* no event, we sleep 5 milliseconds */
- if (hasevent == 0) {
+ /* When there is no event, sleep 5 milliseconds not to use too much CPU when idle.
+ *
+ * Skip sleeping when simulating events so tests don't idle unnecessarily as simulated
+ * events are typically generated from a timer that runs in the main loop. */
+ if ((has_event == false) && !(G.f & G_FLAG_EVENT_SIMULATE)) {
PIL_sleep_ms(5);
}
}
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 7fddf60eb97..af696ddd8f9 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -23,7 +23,6 @@
#pragma once
-struct ReportList;
struct wmWindow;
#include "gizmo/wm_gizmo_wmapi.h"
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index d54090a6025..c7fe07cad7f 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -45,6 +45,9 @@ void wm_homefile_read(struct bContext *C,
void wm_file_read_report(bContext *C, struct Main *bmain);
void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action);
+bool wm_operator_close_file_dialog_if_needed(bContext *C,
+ wmOperator *op,
+ wmGenericCallbackFn exec_fn);
bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm);
void WM_OT_save_homefile(struct wmOperatorType *ot);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c
index 439d611b085..2a67c2bee9f 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr.c
@@ -128,6 +128,11 @@ bool wm_xr_events_handle(wmWindowManager *wm)
if (wm->xr.runtime && wm->xr.runtime->context) {
GHOST_XrEventsHandle(wm->xr.runtime->context);
+ /* Process OpenXR action events. */
+ if (WM_xr_session_is_ready(&wm->xr)) {
+ wm_xr_session_actions_update(&wm->xr);
+ }
+
/* wm_window_process_events() uses the return value to determine if it can put the main thread
* to sleep for some milliseconds. We never want that to happen while the VR session runs on
* the main thread. So always return true. */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_actions.c
new file mode 100644
index 00000000000..51ed3dcfd3c
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actions.c
@@ -0,0 +1,480 @@
+/*
+ * 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 wm
+ *
+ * \name Window-Manager XR Actions
+ *
+ * Uses the Ghost-XR API to manage OpenXR actions.
+ * All functions are designed to be usable by RNA / the Python API.
+ */
+
+#include "BLI_math.h"
+
+#include "GHOST_C-api.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_xr_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name XR-Action API
+ *
+ * API functions for managing OpenXR actions.
+ *
+ * \{ */
+
+static wmXrActionSet *action_set_create(const char *action_set_name)
+{
+ wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__);
+ action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name");
+ strcpy(action_set->name, action_set_name);
+
+ return action_set;
+}
+
+static void action_set_destroy(void *val)
+{
+ wmXrActionSet *action_set = val;
+
+ MEM_SAFE_FREE(action_set->name);
+
+ MEM_freeN(action_set);
+}
+
+static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name)
+{
+ return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name);
+}
+
+static wmXrAction *action_create(const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ wmOperatorType *ot,
+ IDProperty *op_properties,
+ eXrOpFlag op_flag)
+{
+ wmXrAction *action = MEM_callocN(sizeof(*action), __func__);
+ action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name");
+ strcpy(action->name, action_name);
+ action->type = type;
+
+ const unsigned int count = count_subaction_paths;
+ action->count_subaction_paths = count;
+
+ action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count,
+ "XrAction_SubactionPaths");
+ for (unsigned int i = 0; i < count; ++i) {
+ action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1,
+ "XrAction_SubactionPath");
+ strcpy(action->subaction_paths[i], subaction_paths[i]);
+ }
+
+ size_t size;
+ switch (type) {
+ case XR_BOOLEAN_INPUT:
+ size = sizeof(bool);
+ break;
+ case XR_FLOAT_INPUT:
+ size = sizeof(float);
+ break;
+ case XR_VECTOR2F_INPUT:
+ size = sizeof(float) * 2;
+ break;
+ case XR_POSE_INPUT:
+ size = sizeof(GHOST_XrPose);
+ break;
+ case XR_VIBRATION_OUTPUT:
+ return action;
+ }
+ action->states = MEM_calloc_arrayN(count, size, "XrAction_States");
+ action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev");
+
+ if (float_threshold) {
+ BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT);
+ action->float_threshold = *float_threshold;
+ CLAMP(action->float_threshold, 0.0f, 1.0f);
+ }
+
+ action->ot = ot;
+ action->op_properties = op_properties;
+ action->op_flag = op_flag;
+
+ return action;
+}
+
+static void action_destroy(void *val)
+{
+ wmXrAction *action = val;
+
+ MEM_SAFE_FREE(action->name);
+
+ const unsigned int count = action->count_subaction_paths;
+ char **subaction_paths = action->subaction_paths;
+ if (subaction_paths) {
+ for (unsigned int i = 0; i < count; ++i) {
+ MEM_SAFE_FREE(subaction_paths[i]);
+ }
+ MEM_freeN(subaction_paths);
+ }
+
+ MEM_SAFE_FREE(action->states);
+ MEM_SAFE_FREE(action->states_prev);
+
+ MEM_freeN(action);
+}
+
+static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ return GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name);
+}
+
+bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name)
+{
+ if (action_set_find(xr, action_set_name)) {
+ return false;
+ }
+
+ wmXrActionSet *action_set = action_set_create(action_set_name);
+
+ GHOST_XrActionSetInfo info = {
+ .name = action_set_name,
+ .customdata_free_fn = action_set_destroy,
+ .customdata = action_set,
+ };
+
+ if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) {
+ return false;
+ }
+
+ return true;
+}
+
+void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return;
+ }
+
+ wmXrSessionState *session_state = &xr->runtime->session_state;
+
+ if (action_set == session_state->active_action_set) {
+ if (action_set->controller_pose_action) {
+ wm_xr_session_controller_data_clear(session_state);
+ action_set->controller_pose_action = NULL;
+ }
+ if (action_set->active_modal_action) {
+ action_set->active_modal_action = NULL;
+ }
+ session_state->active_action_set = NULL;
+ }
+
+ GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name);
+}
+
+bool WM_xr_action_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ wmOperatorType *ot,
+ IDProperty *op_properties,
+ eXrOpFlag op_flag)
+{
+ if (action_find(xr, action_set_name, action_name)) {
+ return false;
+ }
+
+ wmXrAction *action = action_create(action_name,
+ type,
+ count_subaction_paths,
+ subaction_paths,
+ float_threshold,
+ ot,
+ op_properties,
+ op_flag);
+
+ GHOST_XrActionInfo info = {
+ .name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ .states = action->states,
+ .customdata_free_fn = action_destroy,
+ .customdata = action,
+ };
+
+ switch (type) {
+ case XR_BOOLEAN_INPUT:
+ info.type = GHOST_kXrActionTypeBooleanInput;
+ break;
+ case XR_FLOAT_INPUT:
+ info.type = GHOST_kXrActionTypeFloatInput;
+ break;
+ case XR_VECTOR2F_INPUT:
+ info.type = GHOST_kXrActionTypeVector2fInput;
+ break;
+ case XR_POSE_INPUT:
+ info.type = GHOST_kXrActionTypePoseInput;
+ break;
+ case XR_VIBRATION_OUTPUT:
+ info.type = GHOST_kXrActionTypeVibrationOutput;
+ break;
+ }
+
+ if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) {
+ return false;
+ }
+
+ return true;
+}
+
+void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return;
+ }
+
+ if (action_set->controller_pose_action &&
+ STREQ(action_set->controller_pose_action->name, action_name)) {
+ if (action_set == xr->runtime->session_state.active_action_set) {
+ wm_xr_session_controller_data_clear(&xr->runtime->session_state);
+ }
+ action_set->controller_pose_action = NULL;
+ }
+ if (action_set->active_modal_action &&
+ STREQ(action_set->active_modal_action->name, action_name)) {
+ action_set->active_modal_action = NULL;
+ }
+
+ wmXrAction *action = action_find(xr, action_set_name, action_name);
+ if (!action) {
+ return;
+ }
+}
+
+bool WM_xr_action_space_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const wmXrPose *poses)
+{
+ GHOST_XrActionSpaceInfo info = {
+ .action_name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ };
+
+ GHOST_XrPose *ghost_poses = MEM_malloc_arrayN(
+ count_subaction_paths, sizeof(*ghost_poses), __func__);
+ for (unsigned int i = 0; i < count_subaction_paths; ++i) {
+ const wmXrPose *pose = &poses[i];
+ GHOST_XrPose *ghost_pose = &ghost_poses[i];
+ copy_v3_v3(ghost_pose->position, pose->position);
+ copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat);
+ }
+ info.poses = ghost_poses;
+
+ bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true :
+ false;
+ MEM_freeN(ghost_poses);
+ return ret;
+}
+
+void WM_xr_action_space_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths)
+{
+ GHOST_XrActionSpaceInfo info = {
+ .action_name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ };
+
+ GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info);
+}
+
+bool WM_xr_action_binding_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths)
+{
+ GHOST_XrActionBindingInfo binding_info = {
+ .action_name = action_name,
+ .count_interaction_paths = count_interaction_paths,
+ .interaction_paths = interaction_paths,
+ };
+
+ GHOST_XrActionProfileInfo profile_info = {
+ .profile_path = profile_path,
+ .count_bindings = 1,
+ .bindings = &binding_info,
+ };
+
+ return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+}
+
+void WM_xr_action_binding_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths)
+{
+ GHOST_XrActionBindingInfo binding_info = {
+ .action_name = action_name,
+ .count_interaction_paths = count_interaction_paths,
+ .interaction_paths = interaction_paths,
+ };
+
+ GHOST_XrActionProfileInfo profile_info = {
+ .profile_path = profile_path,
+ .count_bindings = 1,
+ .bindings = &binding_info,
+ };
+
+ GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+}
+
+bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return false;
+ }
+
+ {
+ /* Unset active modal action (if any). */
+ wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set;
+ if (active_action_set) {
+ wmXrAction *active_modal_action = active_action_set->active_modal_action;
+ if (active_modal_action) {
+ if (active_modal_action->active_modal_path) {
+ active_modal_action->active_modal_path = NULL;
+ }
+ active_action_set->active_modal_action = NULL;
+ }
+ }
+ }
+
+ xr->runtime->session_state.active_action_set = action_set;
+
+ if (action_set->controller_pose_action) {
+ wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr);
+ }
+
+ return true;
+}
+
+bool WM_xr_controller_pose_action_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return false;
+ }
+
+ wmXrAction *action = action_find(xr, action_set_name, action_name);
+ if (!action) {
+ return false;
+ }
+
+ action_set->controller_pose_action = action;
+
+ if (action_set == xr->runtime->session_state.active_action_set) {
+ wm_xr_session_controller_data_populate(action, xr);
+ }
+
+ return true;
+}
+
+bool WM_xr_action_state_get(const wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path,
+ wmXrActionState *r_state)
+{
+ const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name);
+ if (!action) {
+ return false;
+ }
+
+ BLI_assert(action->type == (eXrActionType)r_state->type);
+
+ /* Find the action state corresponding to the subaction path. */
+ for (unsigned int i = 0; i < action->count_subaction_paths; ++i) {
+ if (STREQ(subaction_path, action->subaction_paths[i])) {
+ switch ((eXrActionType)r_state->type) {
+ case XR_BOOLEAN_INPUT:
+ r_state->state_boolean = ((bool *)action->states)[i];
+ break;
+ case XR_FLOAT_INPUT:
+ r_state->state_float = ((float *)action->states)[i];
+ break;
+ case XR_VECTOR2F_INPUT:
+ copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]);
+ break;
+ case XR_POSE_INPUT: {
+ const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i];
+ copy_v3_v3(r_state->state_pose.position, pose->position);
+ copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat);
+ break;
+ }
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool WM_xr_haptic_action_apply(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const long long *duration,
+ const float *frequency,
+ const float *amplitude)
+{
+ return GHOST_XrApplyHapticAction(
+ xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ?
+ true :
+ false;
+}
+
+void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name);
+}
+
+/** \} */ /* XR-Action API */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index cc4a7e41e82..1f722855696 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -45,6 +45,12 @@ void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4])
translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
+void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
+{
+ quat_to_mat4(r_mat, pose->orientation_quat);
+ copy_v3_v3(r_mat[3], pose->position);
+}
+
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
const XrSessionSettings *session_settings,
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index 25e3da3ffb4..9bf63be61dd 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -24,6 +24,21 @@
#include "wm_xr.h"
+struct wmXrActionSet;
+
+typedef struct wmXrControllerData {
+ /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
+ This subaction path will later be combined with a component path, and that combined path should
+ also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
+ /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
+ */
+ char subaction_path[64];
+ /** Last known controller pose (in world space) stored for queries. */
+ GHOST_XrPose pose;
+ /** The last known controller matrix, calculated from above's controller pose. */
+ float mat[4][4];
+} wmXrControllerData;
+
typedef struct wmXrSessionState {
bool is_started;
@@ -39,11 +54,23 @@ typedef struct wmXrSessionState {
Object *prev_base_pose_object;
/** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */
int prev_settings_flag;
+ /** Copy of wmXrDrawData.base_pose. */
+ GHOST_XrPose prev_base_pose;
+ /** Copy of GHOST_XrDrawViewInfo.local_pose. */
+ GHOST_XrPose prev_local_pose;
/** Copy of wmXrDrawData.eye_position_ofs. */
float prev_eye_position_ofs[3];
bool force_reset_to_base_pose;
bool is_view_data_set;
+
+ /** Last known controller data. */
+ wmXrControllerData controllers[2];
+
+ /** The currently active action set that will be updated on calls to
+ * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and
+ * updated. */
+ struct wmXrActionSet *active_action_set;
} wmXrSessionState;
typedef struct wmXrRuntimeData {
@@ -79,6 +106,40 @@ typedef struct wmXrDrawData {
float eye_position_ofs[3]; /* Local/view space. */
} wmXrDrawData;
+typedef struct wmXrAction {
+ char *name;
+ eXrActionType type;
+ unsigned int count_subaction_paths;
+ char **subaction_paths;
+ /** States for each subaction path. */
+ void *states;
+ /** Previous states, stored to determine XR events. */
+ void *states_prev;
+
+ /** Input threshold for float/vector2f actions. */
+ float float_threshold;
+
+ /** The currently active subaction path (if any) for modal actions. */
+ char **active_modal_path;
+
+ /** Operator to be called on XR events. */
+ struct wmOperatorType *ot;
+ IDProperty *op_properties;
+ eXrOpFlag op_flag;
+} wmXrAction;
+
+typedef struct wmXrActionSet {
+ char *name;
+
+ /** The XR pose action that determines the controller
+ * transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose",
+ * although it could differ depending on the specification and hardware. */
+ wmXrAction *controller_pose_action;
+
+ /** The currently active modal action (if any). */
+ wmXrAction *active_modal_action;
+} wmXrActionSet;
+
wmXrRuntimeData *wm_xr_runtime_data_create(void);
void wm_xr_runtime_data_free(wmXrRuntimeData **runtime);
@@ -95,5 +156,12 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
void *wm_xr_session_gpu_binding_context_create(void);
void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context);
+void wm_xr_session_actions_init(wmXrData *xr);
+void wm_xr_session_actions_update(wmXrData *xr);
+void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action,
+ wmXrData *xr);
+void wm_xr_session_controller_data_clear(wmXrSessionState *state);
+
void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]);
+void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index b9ef40e3398..1ddbe228e05 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -18,7 +18,9 @@
* \ingroup wm
*/
+#include "BKE_callbacks.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -49,11 +51,24 @@ static CLG_LogRef LOG = {"wm.xr"};
/* -------------------------------------------------------------------- */
+static void wm_xr_session_create_cb(void)
+{
+ Main *bmain = G_MAIN;
+ wmWindowManager *wm = bmain->wm.first;
+ wmXrData *xr_data = &wm->xr;
+
+ /* Get action set data from Python. */
+ BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE);
+
+ wm_xr_session_actions_init(xr_data);
+}
+
static void wm_xr_session_exit_cb(void *customdata)
{
wmXrData *xr_data = customdata;
xr_data->runtime->session_state.is_started = false;
+
if (xr_data->runtime->exit_fn) {
xr_data->runtime->exit_fn(xr_data);
}
@@ -65,6 +80,10 @@ static void wm_xr_session_exit_cb(void *customdata)
static void wm_xr_session_begin_info_create(wmXrData *xr_data,
GHOST_XrSessionBeginInfo *r_begin_info)
{
+ /* Callback for when the session is created. This is needed to create and bind OpenXR actions
+ * after the session is created but before it is started. */
+ r_begin_info->create_fn = wm_xr_session_create_cb;
+
/* WM-XR exit function, does some own stuff and calls callback passed to wm_xr_session_toggle(),
* to allow external code to execute its own session-exit logic. */
r_begin_info->exit_fn = wm_xr_session_exit_cb;
@@ -289,6 +308,7 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
/**
* Update information that is only stored for external state queries. E.g. for Python API to
* request the current (as in, last known) viewer pose.
+ * Controller data and action sets will be updated separately via wm_xr_session_actions_update().
*/
void wm_xr_session_state_update(const XrSessionSettings *settings,
const wmXrDrawData *draw_data,
@@ -322,6 +342,8 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
DEFAULT_SENSOR_WIDTH);
copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs);
+ memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose));
+ memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose));
state->prev_settings_flag = settings->flag;
state->prev_base_pose_type = settings->base_pose_type;
state->prev_base_pose_object = settings->base_pose_object;
@@ -373,6 +395,132 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
return true;
}
+bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ zero_v3(r_location);
+ return false;
+ }
+
+ copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position);
+ return true;
+}
+
+bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ unit_qt(r_rotation);
+ return false;
+ }
+
+ copy_v4_v4(r_rotation,
+ xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat);
+ return true;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name XR-Session Actions
+ *
+ * XR action processing and event dispatching.
+ *
+ * \{ */
+
+void wm_xr_session_actions_init(wmXrData *xr)
+{
+ if (!xr->runtime) {
+ return;
+ }
+
+ GHOST_XrAttachActionSets(xr->runtime->context);
+}
+
+static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings,
+ const wmXrAction *controller_pose_action,
+ wmXrSessionState *state)
+{
+ const unsigned int count = (unsigned int)min_ii(
+ (int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers));
+
+ float view_ofs[3];
+ float base_inv[4][4];
+ float tmp[4][4];
+
+ zero_v3(view_ofs);
+ if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
+ add_v3_v3(view_ofs, state->prev_local_pose.position);
+ }
+
+ wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv);
+ invert_m4(base_inv);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ wmXrControllerData *controller = &state->controllers[i];
+
+ /* Calculate controller matrix in world space. */
+ wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp);
+
+ /* Apply eye position and base pose offsets. */
+ sub_v3_v3(tmp[3], view_ofs);
+ mul_m4_m4m4(controller->mat, base_inv, tmp);
+
+ /* Save final pose. */
+ mat4_to_loc_quat(
+ controller->pose.position, controller->pose.orientation_quat, controller->mat);
+ }
+}
+
+void wm_xr_session_actions_update(wmXrData *xr)
+{
+ if (!xr->runtime) {
+ return;
+ }
+
+ GHOST_XrContextHandle xr_context = xr->runtime->context;
+ wmXrSessionState *state = &xr->runtime->session_state;
+ wmXrActionSet *active_action_set = state->active_action_set;
+
+ int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL);
+ if (!ret) {
+ return;
+ }
+
+ /* Only update controller mats for active action set. */
+ if (active_action_set) {
+ if (active_action_set->controller_pose_action) {
+ wm_xr_session_controller_mats_update(
+ &xr->session_settings, active_action_set->controller_pose_action, state);
+ }
+ }
+}
+
+void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr)
+{
+ wmXrSessionState *state = &xr->runtime->session_state;
+
+ const unsigned int count = (unsigned int)min_ii(
+ (int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ wmXrControllerData *c = &state->controllers[i];
+ strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]);
+ memset(&c->pose, 0, sizeof(c->pose));
+ zero_m4(c->mat);
+ }
+}
+
+void wm_xr_session_controller_data_clear(wmXrSessionState *state)
+{
+ memset(state->controllers, 0, sizeof(state->controllers));
+}
+
+/** \} */ /* XR-Session Actions */
+
/* -------------------------------------------------------------------- */
/** \name XR-Session Surface
*
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index c7b940d0012..39bf2f1e32d 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -116,9 +116,14 @@ if(WITH_XR_OPENXR)
endif()
if(WITH_GMP)
+ blender_include_dirs(${GMP_INCLUDE_DIRS})
add_definitions(-DWITH_GMP)
endif()
+if(WITH_OPENCOLORIO)
+ add_definitions(-DWITH_OCIO)
+endif()
+
# Setup the exe sources and buildinfo
set(SRC
creator.c
@@ -130,14 +135,11 @@ set(SRC
# MSVC 2010 gives linking errors with the manifest
if(WIN32 AND NOT UNIX)
- string(SUBSTRING ${BLENDER_VERSION} 0 1 bver1)
- string(SUBSTRING ${BLENDER_VERSION} 2 1 bver2)
- string(SUBSTRING ${BLENDER_VERSION} 3 1 bver3)
add_definitions(
-DBLEN_VER_RC_STR="${BLENDER_VERSION}"
- -DBLEN_VER_RC_1=${bver1}
- -DBLEN_VER_RC_2=${bver2}
- -DBLEN_VER_RC_3=${bver3}
+ -DBLEN_VER_RC_1=${BLENDER_VERSION_MAJOR}
+ -DBLEN_VER_RC_2=${BLENDER_VERSION_MINOR}
+ -DBLEN_VER_RC_3=${BLENDER_VERSION_PATCH}
-DBLEN_VER_RC_4=0
)
@@ -188,7 +190,7 @@ if(WITH_BUILDINFO)
# --------------------------------------------------------------------------
# write header for values that change each build
- # note, generaed file is in build dir's source/creator
+ # note, generated file is in build dir's source/creator
# except when used as an include path.
add_definitions(-DWITH_BUILDINFO_HEADER)
@@ -288,6 +290,15 @@ if(WITH_PYTHON_MODULE)
else()
add_executable(blender ${EXETYPE} ${SRC})
+ if(WIN32)
+ add_executable(blender-launcher WIN32
+ blender_launcher_win32.c
+ ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc
+ ${CMAKE_BINARY_DIR}/blender.exe.manifest
+ )
+ target_compile_definitions (blender-launcher PRIVATE -D_UNICODE -DUNICODE)
+ target_link_libraries(blender-launcher Pathcch.lib)
+ endif()
endif()
if(WITH_BUILDINFO)
@@ -326,7 +337,14 @@ elseif(WIN32)
elseif(APPLE)
if(WITH_PYTHON_MODULE)
- set(TARGETDIR_VER ${BLENDER_VERSION})
+ if(WITH_INSTALL_PORTABLE)
+ set(TARGETDIR_VER $<TARGET_FILE_DIR:blender>/../Resources/${BLENDER_VERSION})
+ # Keep the `BLENDER_VERSION` folder and bpy.so in the build folder.
+ set(INSTALL_BPY_TO_SITE_PACKAGES OFF)
+ else()
+ set(TARGETDIR_VER "${PYTHON_LIBPATH}/Resources/${BLENDER_VERSION}")
+ set(INSTALL_BPY_TO_SITE_PACKAGES ON)
+ endif()
else()
set(TARGETDIR_VER Blender.app/Contents/Resources/${BLENDER_VERSION})
endif()
@@ -456,8 +474,8 @@ if(UNIX AND NOT APPLE)
add_custom_target(
blender_man_page ALL
COMMAND ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py
- ${EXECUTABLE_OUTPUT_PATH}/blender
- ${CMAKE_CURRENT_BINARY_DIR}/blender.1)
+ --blender ${EXECUTABLE_OUTPUT_PATH}/blender
+ --output ${CMAKE_CURRENT_BINARY_DIR}/blender.1)
add_dependencies(blender_man_page blender)
endif()
endif()
@@ -686,6 +704,26 @@ elseif(WIN32)
DESTINATION "."
)
endif()
+ if(MSVC_ASAN)
+ # The asan dll's can be found in the same folder as the compiler, this is the easiest way to find these.
+ string(REPLACE "cl.exe" "clang_rt.asan_dynamic-x86_64.dll" ASAN_DLL ${CMAKE_C_COMPILER})
+ string(REPLACE "cl.exe" "clang_rt.asan_dbg_dynamic-x86_64.dll" ASAN_DEBUG_DLL ${CMAKE_C_COMPILER})
+ if(NOT EXISTS "${ASAN_DLL}")
+ message(FATAL_ERROR "Asan is enabled, but the ASAN runtime is not detected, this is an optional component during the MSVC install, please install it")
+ endif()
+ install(
+ FILES ${ASAN_DLL}
+ DESTINATION "."
+ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
+ )
+ install(
+ FILES ${ASAN_DEBUG_DLL}
+ DESTINATION "."
+ CONFIGURATIONS Debug
+ )
+ unset(ASAN_DLL)
+ unset(ASAN_DEBUG_DLL)
+ endif()
if(WITH_GMP)
install(
@@ -846,13 +884,13 @@ elseif(WIN32)
if(WITH_TBB)
install(
FILES
- ${LIBDIR}/tbb/lib/tbb.dll
+ ${LIBDIR}/tbb/bin/tbb.dll
DESTINATION "."
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
FILES
- ${LIBDIR}/tbb/lib/debug/tbb_debug.dll
+ ${LIBDIR}/tbb/bin/tbb_debug.dll
DESTINATION "."
CONFIGURATIONS Debug
)
@@ -860,18 +898,19 @@ elseif(WIN32)
if(WITH_TBB_MALLOC_PROXY)
install(
FILES
- ${LIBDIR}/tbb/lib/tbbmalloc.dll
- ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll
+ ${LIBDIR}/tbb/bin/tbbmalloc.dll
+ ${LIBDIR}/tbb/bin/tbbmalloc_proxy.dll
DESTINATION "."
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
FILES
- ${LIBDIR}/tbb/lib/debug/tbbmalloc.dll
- ${LIBDIR}/tbb/lib/debug/tbbmalloc_proxy.dll
+ ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll
+ ${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll
DESTINATION "."
CONFIGURATIONS Debug
)
+ list(APPEND LIB ${TBB_MALLOC_LIBRARIES})
endif()
if(WITH_CODEC_SNDFILE)
@@ -1005,6 +1044,20 @@ elseif(APPLE)
FILES ${LIBDIR}/openmp/lib/libomp.dylib
DESTINATION Blender.app/Contents/Resources/lib
)
+ if(WITH_PYTHON_MODULE)
+ # Move the dylib in a Blender version folder to keep the corresponding OpenMP version.
+ install(
+ DIRECTORY ${CMAKE_BINARY_DIR}/Resources/lib
+ DESTINATION ${TARGETDIR_VER}
+ )
+ add_custom_command(TARGET blender POST_BUILD
+ # The old `LC_LOAD_DYLIB` is the `LC_ID_DYLIB` of the LIBDIR OpenMP dylib.
+ # Change it to support multiple rpaths.
+ COMMAND xcrun install_name_tool -change "@executable_path/../Resources/lib/libomp.dylib" "@rpath/libomp.dylib" "$<TARGET_FILE:blender>"
+ # For installation into site-packages.
+ COMMAND xcrun install_name_tool -add_rpath "@loader_path/../Resources/${BLENDER_VERSION}/lib" "$<TARGET_FILE:blender>"
+ )
+ endif()
endif()
if(WITH_LLVM AND NOT LLVM_STATIC)
@@ -1036,6 +1089,14 @@ elseif(APPLE)
)
unset(_py_inc_suffix)
endif()
+ if(WITH_PYTHON_MODULE)
+ if(INSTALL_BPY_TO_SITE_PACKAGES)
+ install(
+ TARGETS blender
+ LIBRARY DESTINATION ${PYTHON_LIBPATH}/site-packages
+ )
+ endif()
+ endif()
if(WITH_DRACO)
install(
@@ -1166,7 +1227,7 @@ endif()
if(WIN32 AND NOT WITH_PYTHON_MODULE)
install(
- TARGETS blender
+ TARGETS blender blender-launcher
COMPONENT Blender
DESTINATION "."
)
diff --git a/source/creator/blender_launcher_win32.c b/source/creator/blender_launcher_win32.c
new file mode 100644
index 00000000000..86b0f4f3b97
--- /dev/null
+++ b/source/creator/blender_launcher_win32.c
@@ -0,0 +1,92 @@
+/*
+ * 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 <Windows.h>
+#include <strsafe.h>
+
+#include <PathCch.h>
+
+int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
+{
+ STARTUPINFO siStartInfo = {0};
+ PROCESS_INFORMATION procInfo;
+ wchar_t path[MAX_PATH];
+
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
+
+ /* Get the path to the currently running executable (blender-launcher.exe) */
+
+ DWORD nSize = GetModuleFileName(NULL, path, MAX_PATH);
+ if (!nSize) {
+ return -1;
+ }
+
+ /* GetModuleFileName returns the number of characters written, but GetLastError needs to be
+ * called to see if it ran out of space or not. However where would we be without exceptions
+ * to the rule: "If the buffer is too small to hold the module name, the function returns nSize.
+ * The last error code remains ERROR_SUCCESS." - source: MSDN. */
+
+ if (GetLastError() == ERROR_SUCCESS && nSize == MAX_PATH) {
+ return -1;
+ }
+
+ /* Remove the filename (blender-launcher.exe) from path. */
+ if (PathCchRemoveFileSpec(path, MAX_PATH) != S_OK) {
+ return -1;
+ }
+
+ /* Add blender.exe to path, resulting in the full path to the blender executable. */
+ if (PathCchCombine(path, MAX_PATH, path, L"blender.exe") != S_OK) {
+ return -1;
+ }
+
+ int required_size_chars = lstrlenW(path) + /* Module name */
+ 3 + /* 2 quotes + Space */
+ lstrlenW(pCmdLine) + /* Original command line */
+ 1; /* Zero terminator */
+ size_t required_size_bytes = required_size_chars * sizeof(wchar_t);
+ wchar_t *buffer = (wchar_t *)malloc(required_size_bytes);
+ if (!buffer) {
+ return -1;
+ }
+
+ if (StringCbPrintfEx(buffer,
+ required_size_bytes,
+ NULL,
+ NULL,
+ STRSAFE_NULL_ON_FAILURE,
+ L"\"%s\" %s",
+ path,
+ pCmdLine) != S_OK) {
+ free(buffer);
+ return -1;
+ }
+
+ BOOL success = CreateProcess(
+ path, buffer, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &procInfo);
+
+ if (success) {
+ /* Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer
+ * needed - MSDN. Closing the handles will NOT terminate the thread/process that we just
+ * started. */
+ CloseHandle(procInfo.hThread);
+ CloseHandle(procInfo.hProcess);
+ }
+
+ free(buffer);
+ return success ? 0 : -1;
+}
diff --git a/source/creator/creator.c b/source/creator/creator.c
index b40718d1f7c..2ec4a2aa616 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -132,7 +132,6 @@ struct ApplicationState app_state = {
/** \name Application Level Callbacks
*
* Initialize callbacks for the modules that need them.
- *
* \{ */
static void callback_mem_error(const char *errorStr)
@@ -211,6 +210,41 @@ char **environ = NULL;
/** \} */
/* -------------------------------------------------------------------- */
+/** \name GMP Allocator Workaround
+ * \{ */
+
+#if (defined(WITH_TBB_MALLOC) && defined(_MSC_VER) && defined(NDEBUG) && defined(WITH_GMP)) || \
+ defined(DOXYGEN)
+# include "gmp.h"
+# include "tbb/scalable_allocator.h"
+
+void *gmp_alloc(size_t size)
+{
+ return scalable_malloc(size);
+}
+void *gmp_realloc(void *ptr, size_t old_size, size_t new_size)
+{
+ return scalable_realloc(ptr, new_size);
+}
+
+void gmp_free(void *ptr, size_t size)
+{
+ scalable_free(ptr);
+}
+/**
+ * Use TBB's scalable_allocator on Windows.
+ * `TBBmalloc` correctly captures all allocations already,
+ * however, GMP is built with MINGW since it doesn't build with MSVC,
+ * which TBB has issues hooking into automatically.
+ */
+void gmp_blender_init_allocator()
+{
+ mp_set_memory_functions(gmp_alloc, gmp_realloc, gmp_free);
+}
+#endif
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Main Function
* \{ */
@@ -343,6 +377,10 @@ int main(int argc,
CCL_init_logging(argv[0]);
#endif
+#if defined(WITH_TBB_MALLOC) && defined(_MSC_VER) && defined(NDEBUG) && defined(WITH_GMP)
+ gmp_blender_init_allocator();
+#endif
+
main_callback_setup();
#if defined(__APPLE__) && !defined(WITH_PYTHON_MODULE) && !defined(WITH_HEADLESS)
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index d1899cc1408..43f23510927 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -674,6 +674,9 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
printf(" $BLENDER_USER_DATAFILES Directory for user data files (icons, translations, ..).\n");
printf(" $BLENDER_SYSTEM_DATAFILES Directory for system wide data files.\n");
printf(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n");
+# ifdef WITH_OCIO
+ printf(" $OCIO Path to override the OpenColorIO config file.\n");
+# endif
# ifdef WIN32
printf(" $TEMP Store temporary files here.\n");
# else
@@ -1189,18 +1192,23 @@ static const char arg_handle_playback_mode_doc[] =
"\t-s <frame>\n"
"\t\tPlay from <frame>.\n"
"\t-e <frame>\n"
- "\t\tPlay until <frame>.";
+ "\t\tPlay until <frame>.\n"
+ "\t-c <cache_memory>\n"
+ "\t\tAmount of memory in megabytes to allow for caching images during playback.\n"
+ "\t\tZero disables (clamping to a fixed number of frames instead).";
static int arg_handle_playback_mode(int argc, const char **argv, void *UNUSED(data))
{
- /* not if -b was given first */
+ /* Ignore the animation player if `-b` was given first. */
if (G.background == 0) {
# ifdef WITH_FFMPEG
/* Setup FFmpeg with current debug flags. */
IMB_ffmpeg_init();
# endif
- WM_main_playanim(argc, argv); /* not the same argc and argv as before */
- exit(0); /* 2.4x didn't do this */
+ /* This function knows to skip this argument ('-a'). */
+ WM_main_playanim(argc, argv);
+
+ exit(0);
}
return -2;
@@ -1951,7 +1959,9 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data)
}
BLI_strncpy(filename, argv[0], sizeof(filename));
+ BLI_path_slash_native(filename);
BLI_path_abs_from_cwd(filename, sizeof(filename));
+ BLI_path_normalize(NULL, filename);
/* load the file */
BKE_reports_init(&reports, RPT_PRINT);
@@ -2041,6 +2051,15 @@ void main_args_setup(bContext *C, bArgs *ba)
BLI_args_add(ba, "-t", "--threads", CB(arg_handle_threads_set), NULL);
+ /* Include in the environment pass so it's possible display errors initializing subsystems,
+ * especially `bpy.appdir` since it's useful to show errors finding paths on startup. */
+ BLI_args_add(ba, NULL, "--log", CB(arg_handle_log_set), ba);
+ BLI_args_add(ba, NULL, "--log-level", CB(arg_handle_log_level_set), ba);
+ BLI_args_add(ba, NULL, "--log-show-basename", CB(arg_handle_log_show_basename_set), ba);
+ BLI_args_add(ba, NULL, "--log-show-backtrace", CB(arg_handle_log_show_backtrace_set), ba);
+ BLI_args_add(ba, NULL, "--log-show-timestamp", CB(arg_handle_log_show_timestamp_set), ba);
+ BLI_args_add(ba, NULL, "--log-file", CB(arg_handle_log_file_set), ba);
+
/* Pass: Background Mode & Settings
*
* Also and commands that exit after usage. */
@@ -2062,13 +2081,6 @@ void main_args_setup(bContext *C, bArgs *ba)
BLI_args_add(ba, "-a", NULL, CB(arg_handle_playback_mode), NULL);
- BLI_args_add(ba, NULL, "--log", CB(arg_handle_log_set), ba);
- BLI_args_add(ba, NULL, "--log-level", CB(arg_handle_log_level_set), ba);
- BLI_args_add(ba, NULL, "--log-show-basename", CB(arg_handle_log_show_basename_set), ba);
- BLI_args_add(ba, NULL, "--log-show-backtrace", CB(arg_handle_log_show_backtrace_set), ba);
- BLI_args_add(ba, NULL, "--log-show-timestamp", CB(arg_handle_log_show_timestamp_set), ba);
- BLI_args_add(ba, NULL, "--log-file", CB(arg_handle_log_file_set), ba);
-
BLI_args_add(ba, "-d", "--debug", CB(arg_handle_debug_mode_set), ba);
# ifdef WITH_FFMPEG
diff --git a/source/creator/creator_intern.h b/source/creator/creator_intern.h
index bcc8a15355a..2260da8db11 100644
--- a/source/creator/creator_intern.h
+++ b/source/creator/creator_intern.h
@@ -72,7 +72,7 @@ enum {
/* for the callbacks: */
#ifndef WITH_PYTHON_MODULE
-# define BLEND_VERSION_FMT "Blender %d.%02d.%d"
+# define BLEND_VERSION_FMT "Blender %d.%d.%d"
# define BLEND_VERSION_ARG (BLENDER_VERSION / 100), (BLENDER_VERSION % 100), BLENDER_VERSION_PATCH
#endif
diff --git a/source/tools b/source/tools
-Subproject b66c22e1fb977bf8dd3797ebedc28fbe28f0305
+Subproject 2afbb8ec472cac5102eb239f57b006f8c938768